can.
can.

Reputation: 2239

How to download files using javascript asynchronously?

I'm building a Chrome extension to download a series of files from a site. The downloading function is derived from How to save a file from a URL with JavaScript.

The program structure is like:

function download()
{
  while(there are still files to download)
  {
    saveFile(url);
  }
}

But I find that all the files are actually written to disk at once after download() returns. And the addresses of those files start with blob: when examine from Chrome's downloads manager.

I wonder if I make the call to saveFile asynchronously, those files could be written one at a time.

Upvotes: 2

Views: 16579

Answers (1)

Esailija
Esailija

Reputation: 140220

Using promises, which are available in Chrome out of the box, you can define the functions like so:

// Download a file form a url.
function saveFile(url) {
  return new Promise(function(resolve, reject) {
    // Get file name from url.
    var xhr = new XMLHttpRequest();
    xhr.responseType = 'blob';
    xhr.onload = function() {
      resolve(xhr);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  }).then(function(xhr) {
    var filename = url.substring(url.lastIndexOf("/") + 1).split("?")[0];
    var a = document.createElement('a');
    a.href = window.URL.createObjectURL(xhr.response); // xhr.response is a blob
    a.download = filename; // Set the file name.
    a.style.display = 'none';
    document.body.appendChild(a);
    a.click();
    return xhr;
  });
}

function download(urls) {
  return Promise.all(urls.map(saveFile));
}

Using it:

download.then(function() {
  alert("all files downloaded");
}).catch(function(e) {
  alert("something went wrong: " + e);
});

If you want to wait for 1 file to download before proceeding with next, the download function should be written like:

function download(urls) {
  var cur = Promise.resolve();
  urls.forEach(function(url) {
    cur = cur.then(function() {
      return saveFile(url);
    });
  });
  return cur;
}

Usage is same as before.

Upvotes: 9

Related Questions