Saving generated file on user drive


(Juraj Masiar) #1

Hello,

I have an export feature in my add-on that saves user data into a file.
However it seems to work only in Windows and not in Ubuntu (the Save file dialog won’t be shown instead the page with text data will be opened).
Is there a way to make saving files working on all platforms? Also I don’t want to use Download API for this as it would require a new permission.

Currently I have two distinct methods for this and both of them has the same issue:

function downloadFile(filename, textData) {
  const node = buildElement('a', {href: `data:text/plain;charset=utf-8,${encodeURIComponent(textData)}`, download: filename, style: 'display: none'});
  document.body.appendChild(node);
  node.click();
  document.body.removeChild(node);
}
function createDownloadFileLink(aNode, fileName, extension, data) {
  aNode.download = `${fileName}.${extension}`;
  aNode.href = URL.createObjectURL(new Blob([JSON.stringify(data)], {type : 'application/json'}));
  aNode.dataset.downloadurl = `${extension}:${aNode.download}:${aNode.href}`;
}

(Martin Giger) #2

That sounds like an issue with the download action assigned to the mime type in the Ubuntu profile.


(Juraj Masiar) #3

Good point. However this is most likely the default behavior as my Ubuntu is completely clean and new. Also users are reporting this bug from other Linux distributions as well.
So by default it’s broken…

Can I force it from my side?
Now I remember there is a header for this called “Content-disposition” that controls how the content should be handled. Maybe I can use something similar here?


(Martin Giger) #4

The most foolproof way here is to use https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/downloads/download.


(rugk) #5

As a side note: In case this is a feature only needed for some actions, just consider requesting the permission on demand, i.e. when the user clicks on “download”. Then it should also be obvious to the user, why it is demanded.


(Juraj Masiar) #6

Thank you for this info! I was not aware of this feature of “optional_permissions”. This is great news, I’m sure I will use this at least for my planned notifications support!

Anyway the support for downloads API is only in Firefox 60 and I was hoping for solution working in current ESR (one of the users reported the bug from ESR version).


(rugk) #7

It is not, it seems to be supported since Firefox 47 in it’s basic form.
See:

Also, BTW, FF 60 is the current ESR version.


(Juraj Masiar) #8

Yes but the optional permission has downloads permission supported only in FF 60:

Regarding ESR - the old one is still available and supported (and probably not automatically upgraded until FF 62 release - there is 2 versions of qualification period), see the release calendar:
https://wiki.mozilla.org/Release_Management/Calendar


(jscher2000) #9

You could look at how it is done in this library:

I’ve used it in web pages, but not in a background script.


(Juraj Masiar) #10

Thank you! Somehow they managed to make it work even in Ubuntu (so it is possible). But the source code is quite unreadable, I will need to dig in deeper to find out what makes it tick :slight_smile:


(rugk) #11

AFAIK basically it just appends a link or so to the DOM and triggers/clicks it.


(Juraj Masiar) #12

Well that sounds like my first function in my original post (I have there custom function buildElement but that just creates an ‘a’ element with properties send in object).

There is probably some small but important difference… I still didin’t had time to look at it.

For example take this function and copy it to console:

function downloadFile(filename, textData) {
  const node = document.createElement('a');
  node.href = `data:text/plain;charset=utf-8,${encodeURIComponent(textData)}`;
  node.download = filename;
  node.style.display = 'none';
  document.body.appendChild(node);
  node.click();
  document.body.removeChild(node);
}

And then execute:
downloadFile('a', 'b')

This will show the save file dialog in Windows but it will open the file in Ubuntu :frowning: