Oakster
Oakster

Reputation: 19

JS to download multiple files

My goal is to have a script that downloads all files inside the given array:

    var links = ['http://file-examples.com/wp-content/uploads/2017/10/file_example_JPG_100kB.jpg',
'http://file-examples.com/wp-content/uploads/2017/10/file_example_TIFF_1MB.tiff'];

function downloadAll(urls) {
  var link = document.createElement('a');
  link.setAttribute('download', null);
  link.style.display = 'none';
  document.body.appendChild(link);
  for (var i = 0; i < urls.length; i++) {
    link.setAttribute('href', urls[i]);
    link.click();
  }
  document.body.removeChild(link);
}
downloadAll(window.links)

This was working smoothly until (I believe) the latest chrome updates.

I've been gathering info around but it seems there's no fix available yet.

Can someone gimme a help here please? Basically want to have a multiple file download script working in JS.

Thanks in advance for the help!

Upvotes: 1

Views: 3022

Answers (1)

Ariwibawa
Ariwibawa

Reputation: 667

Using File System Access API, we can archive the same goal.

async function DownloadFiles(paramToFiles) {
   try {
       var dirHandle = await window.showDirectoryPicker({
           startIn: 'videos',//default folder
           mode: 'readwrite'//ask for write permission
       });//move script from function startDownload to here, because of an error "SecurityError: Failed to execute 'showDirectoryPicker' on 'Window': Must be handling a user gesture to show a file picker.". It was working on localhost.
       for (var index in paramToFiles.Files) {
          var file = paramToFiles.Files[index];
          const fileHandle = await dirHandle.getFileHandle(file.FileName, { create: true });
          if (await verifyPermission(fileHandle, true)) {
              const writable = await fileHandle.createWritable();
              await writable.write(await getBlob(file.URL));
              await writable.close();
          }
      }
   }
   catch (error) {
       alert(error);
   }
   return false;
}
async function startDownload(dirHandle, paramToFiles) {
   //move above
}
async function verifyPermission(fileHandle, readWrite) {
   const options = {};
   if (readWrite) {
       options.mode = 'readwrite';
   }
   if ((await fileHandle.queryPermission(options)) === 'granted') {
       return true;
   }
   if ((await fileHandle.requestPermission(options)) === 'granted') {
       return true;
   }
   return false;
}
function getBlob(urlToGet) {
   const blob = fetch(urlToGet).then(data => data.blob());
   return blob;
}

You can call it like this.

<script>
   function doDownload() {
      DownloadFiles({
         'Files': [
            {
               'FileName': 'Download-Nih-01.zip',
               'URL': '/Download/Download-Nih-01.zip'
            },
            {
               'FileName': 'Download-Nih-02.zip',
               'URL': '/Download/Download-Nih-02.zip'
            },
            {
               'FileName': 'Download-Nih-03.zip',
               'URL': '/Download/Download-Nih-03.zip'
            }
         ]
      });
   ​}
   ​const butDow = document.getElementById('doDownload');
   ​butDow.addEventListener('click', async () => doDownload())
​</script>

A few things to note about this implementation:

  1. This only works if your site is under HTTPS, if you try to access it with HTTP it will thrown an error TypeError: window.showDirectoryPicker is not a function.
  2. This is experimental, so expect behavior change in the future.
  3. User must make action before executing this script (ex: click a button), and make sure that action doesn't postback.

This was button declaration that I used to test this.

<button type="button" id="doDownload">Download</button>

As of this writing (18-Aug-2021), this API is supported by chrome, edge and opera. I've tested it using ms edge (chromium) and its working.

ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/showDirectoryPicker

Upvotes: 2

Related Questions