# Export StorageArea.get() using downloads.download()

**URL:** <https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388>\
**Category:** Development\
**Created:** [October 12, 2017, 10:45pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388 "2017-10-12T22:45:30Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![ericjung1](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/ericjung1/32/15471_2.png) [@ericjung1](https://discourse.mozilla.org/u/ericjung1)\
**Post date:** [October 12, 2017, 10:45pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/1 "2017-10-12T22:45:30Z")

</div>

Hi,

I know I can use [downloads.download()](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/downloads/download) to provide users with a way to “export” addon settings in the [StorageArea](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/StorageArea/get) to their local filesystem. But I’m unable to find any examples or see how to do this.

Any suggestions?

thanks,  
eric

---

<div class="post-metadata">

**Author:** ![mig](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/mig/32/30725_2.png) [@mig](https://discourse.mozilla.org/u/mig)\
**Post date:** [October 12, 2017, 11:27pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/2 "2017-10-12T23:27:15Z")

</div>

Hey Eric,

I copied a piece of code i use in VDH: [https://pastebin.com/90BJWHsY](https://pastebin.com/90BJWHsY)

In my case, i create an empty file because i am only interested in getting the full path and reserving the unique file name, but you can change the line:

```auto
var file = new File(new Array(),fileName);

```

to put some actual content in your file.  
Note that this code is a bit more complicated than what you need if you only want to support Firefox. Chrome does things differently.

Hope this helps.  
/mig

EDIT: this just addresses exporting the data to a file, not getting data from the storage area which should be simpler.

---

<div class="post-metadata">

**Author:** ![ericjung1](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/ericjung1/32/15471_2.png) [@ericjung1](https://discourse.mozilla.org/u/ericjung1)\
**Post date:** [October 13, 2017, 12:25am UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/3 "2017-10-13T00:25:42Z")

</div>

Mig, thanks! Hope you are well.

Of course I would like to support Chrome, too, so thank you for the complete example. The call to browser.downloads.search() is strange. Why do that?

---

<div class="post-metadata">

**Author:** ![mig](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/mig/32/30725_2.png) [@mig](https://discourse.mozilla.org/u/mig)\
**Post date:** [October 13, 2017, 1:17am UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/4 "2017-10-13T01:17:26Z")

</div>

I call `browser.downloads.search` because `browser.downloads.download` returns, through a Promise, a `downloadItemId`, but what i’m really interested in is the field `filename` of the downloadItem which contains the full path to the chosen file.

It is interesting to note that on Firefox, `browser.downloads.download` resolves after the user has chosen the file (assuming field `saveas` is `true`), so you need to call `browser.downloads.search` to get the details. On Chrome, `browser.downloads.download` resolves immediately, and you get a notification with downloadItem details through the listener when the file name is chosen by the user.

You should also keep in mind that on Firefox, if the user cancels the SaveAs dialog, `browser.downloads.download` rejects the promise, while in Chrome, you receive an event with an error.

Those differences make the code more complicated than what it should be. If you are only interested in resolving the promise once the file has been written, you can simplify the whole thing by just installing a `downloads.onChanged` listener and wait for a change where your downloadItem state is “complete” (`details.state && details.state.current === "complete"`)

Something like this should do it (both Chrome & Firefox):

```auto

function SaveFile(fileName, content) {
  return new Promise((resolve, reject) => {
    var file = new File(new Array(content), fileName);
    browser.downloads.download({
      url: URL.createObjectURL(file),
      conflictAction: "uniquify",
      filename: fileName,
      saveAs: true,
    })
      .then((downloadId) => {
        function Listener(details) {
          if (details.id == downloadId)
            if (details.error)
              resolve(false);
            else if (details.state) {
              if (details.state.current != "in_progress") {
                browser.downloads.onChanged.removeListener(Listener);
                resolve(details.state.current == "complete");
              }
            }
        }
        browser.downloads.onChanged.addListener(Listener);
      })
      .catch((err) => {
        resolve(false);
      });
  });
}

SaveFile("test.txt", "Toto a faim")
  .then((result) => {
    console.info("SaveFile ok", result);
  })
  .catch((err) => {
    console.info("SaveFile ko", err);
  });

```

---

<div class="post-metadata">

**Author:** ![erosman](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/erosman/32/15447_2.png) [@erosman](https://discourse.mozilla.org/u/erosman)\
**Post date:** [October 13, 2017, 5:17am UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/5 "2017-10-13T05:17:48Z")

</div>

> [@ericjung1](#):
>
> I know I can use downloads.download() to provide users with a way to “export” addon settings in the StorageArea to their local filesystem. But I’m unable to find any examples or see how to do this.

Check one of my addons with Export feature (e.g Edit, FoxyImage, Historia, etc)

I have a modular code that I copy/paste into all of them. 😉

---

<div class="post-metadata">

**Author:** ![ericjung1](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/ericjung1/32/15471_2.png) [@ericjung1](https://discourse.mozilla.org/u/ericjung1)\
**Post date:** [October 13, 2017, 10:19pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/6 "2017-10-13T22:19:56Z")

</div>

> [@erosman](#):
>
> I have a modular code that I copy/paste into all of them.

Thank you! I get this error on the Blob constructor:

> TypeError: Argument 1 of Blob.constructor can’t be converted to a sequence

Here is my code:

```
getAllSettings().then((settings) => {
  let blob = new Blob(JSON.stringify([settings], null, 2), {type : 'text/json'});
  let filename = "some-settings.json";
  chrome.downloads.download({
    url: URL.createObjectURL(blob),
    filename,
    saveAs: true,
    conflictAction: 'uniquify'
  });
});

```

[Here is the output of JSON.stringify([settings], null, 2)](https://pastebin.mozilla.org/9070072).

I’ve used database blobs before but not [webAPI blobs](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob) before. Any ideas what I’m doing wrong? I’m also slightly confused at the syntax for filename. I thought you’d need a keyName/colon prefix like for url, saveAs, and conflictAction.

Thanks,  
Eric

---

<div class="post-metadata">

**Author:** ![mig](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/mig/32/30725_2.png) [@mig](https://discourse.mozilla.org/u/mig)\
**Post date:** [October 13, 2017, 10:48pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/7 "2017-10-13T22:48:11Z")

</div>

I believe the first argument of `new Blob(...)` should be an array, so you should try:

```auto
let blob = new Blob([JSON.stringify([settings], null, 2)], {type : 'text/json'});

```

---

<div class="post-metadata">

**Author:** ![ericjung1](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/ericjung1/32/15471_2.png) [@ericjung1](https://discourse.mozilla.org/u/ericjung1)\
**Post date:** [October 13, 2017, 11:20pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/8 "2017-10-13T23:20:27Z")

</div>

> [@mig](#):
>
> I believe the first argument of new Blob(…) should be an array,

Indeed, you are right. I made the 1st arg to JSON.stringify an array instead. But now I get a different error. `chrome.downloads` is not defined; yeah, i know that. Ok, so I change `chrome` to `browser` but then get `browser.downloads` is undefined. This is Firefox 58 (nightly). I know `browser` is defined in this module because i’m using it elsewhere in the same module. It’s an options module. What gives?

---

<div class="post-metadata">

**Author:** ![mig](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/mig/32/30725_2.png) [@mig](https://discourse.mozilla.org/u/mig)\
**Post date:** [October 13, 2017, 11:35pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/9 "2017-10-13T23:35:17Z")

</div>

You should use the [webtension-polyfill library](https://github.com/mozilla/webextension-polyfill), then you can use `browser` everywhere. All Chrome API calls like `chrome.api.fnt(...args,callback)` should then be written `browser.api.fnt(...args)` with the function returning a Promise instead of invoking the callback.

---

<div class="post-metadata">

**Author:** ![ericjung1](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/ericjung1/32/15471_2.png) [@ericjung1](https://discourse.mozilla.org/u/ericjung1)\
**Post date:** [October 13, 2017, 11:44pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/10 "2017-10-13T23:44:00Z")

</div>

> [@mig](#):
>
> You should use the webtension-polyfill library, then you can use browser everywhere.

OK, but I dont think this is related to polyfill use. I’m in Firefox and using the browser object, not Google chrome or the chrome object. Right now, this is strictly a Firefox-only addon.

---

<div class="post-metadata">

**Author:** ![mig](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/mig/32/30725_2.png) [@mig](https://discourse.mozilla.org/u/mig)\
**Post date:** [October 13, 2017, 11:49pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/11 "2017-10-13T23:49:55Z")

</div>

Sorry, i misread your issue.

Your problem may be that you did not include `downloads` in the `manifest.json` permissions.

---

<div class="post-metadata">

**Author:** ![erosman](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/erosman/32/15447_2.png) [@erosman](https://discourse.mozilla.org/u/erosman)\
**Post date:** [October 14, 2017, 5:25am UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/12 "2017-10-14T05:25:07Z")

</div>

> [@ericjung1](#):
>
> let blob = new Blob(JSON.stringify([settings], null, 2)

The error is here. You are passing a string while the blob wants array.

`let blob = new Blob(JSON.stringify([settings], null, 2),.......`

Try this:

```
getAllSettings().then((settings) => {
  
  let data = JSON.stringify(settings, null, 2); 
  let blob = new Blob([data], {type : 'text/json'});
  let filename = 'some-settings.json';
  chrome.downloads.download({
    url: URL.createObjectURL(blob),
    filename,
    saveAs: true,
    conflictAction: 'uniquify'
  });
});

```

Or if you prefer to combine them:

getAllSettings().then((settings) =\> {

```
  let blob = new Blob([JSON.stringify(settings, null, 2)], {type : 'text/json'});
  let filename = 'some-settings.json';
  chrome.downloads.download({
    url: URL.createObjectURL(blob),
    filename,
    saveAs: true,
    conflictAction: 'uniquify'
  });
});

```

I also had to find this by trial and error at the time. 😉

Have you added “downloads” to permissions?

Where are you running the code? The API is not available in content scripts.

---

<div class="post-metadata">

**Author:** ![ericjung1](https://sea1.discourse-cdn.com/flex001/user_avatar/discourse.mozilla.org/ericjung1/32/15471_2.png) [@ericjung1](https://discourse.mozilla.org/u/ericjung1)\
**Post date:** [October 15, 2017, 6:13pm UTC](https://discourse.mozilla.org/t/export-storagearea-get-using-downloads-download/20388/13 "2017-10-15T18:13:58Z")

</div>

> [@mig](#):
>
> Your problem is that you did not include downloads in the manifest.json permissions.

Yes! Thank you so much.
