最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How in JS to download more than 10 files in browser including Firefox - Stack Overflow

programmeradmin1浏览0评论

I try the following code to download multiple files at once:

var urls = [...];

for(var i = 0; i < urls.length; i++) {

    var tempLink = document.createElement('a');
    tempLink.setAttribute('href', urls[i]);
    tempLink.setAttribute('download', urls[i].split('/')[urls[i].split('/').length*1-1*1]);
    tempLink.click();
}

And there are two problems:

1. Chrome and Opera downloads only 10 files at once

2. In Firefox the code doesn't work at all

How to resolve both problems?

ps. I know how to zip all files with server side, but I'm interested in a solution in JS for static websites without server side

I try the following code to download multiple files at once:

var urls = [...];

for(var i = 0; i < urls.length; i++) {

    var tempLink = document.createElement('a');
    tempLink.setAttribute('href', urls[i]);
    tempLink.setAttribute('download', urls[i].split('/')[urls[i].split('/').length*1-1*1]);
    tempLink.click();
}

And there are two problems:

1. Chrome and Opera downloads only 10 files at once

2. In Firefox the code doesn't work at all

How to resolve both problems?

ps. I know how to zip all files with server side, but I'm interested in a solution in JS for static websites without server side

Share Improve this question edited May 22, 2019 at 13:59 stckvrw asked May 21, 2019 at 19:04 stckvrwstckvrw 1,81123 silver badges54 bronze badges 11
  • 2 i've downloaded 1000s of files like this in chrome, but i had to stagger the downloads using a setTimeout and semi-generous timings to make sure it didn't back up (there's no callback to make it really async). feed the timeout something like i*333 for the 2nd argument. – dandavis Commented May 21, 2019 at 19:06
  • @dandavis thanks! What about Firefox? – stckvrw Commented May 21, 2019 at 19:13
  • If you have access to the server, it might be a better idea to send the server a list of files to download, have the server zip them together, then it sends you the zipped file. Even if it just sends back the file location for you to download "manually" in your script, that's better than trying to manage time delays between multiple downloads. Ex. in C#: c-sharpcorner./article/…, of course, use the language you need. – putercarguy Commented May 21, 2019 at 19:13
  • @putercarguy yes, I know how to zip, but I would like to find a solution for static websites without server side – stckvrw Commented May 21, 2019 at 19:15
  • 1 Zipping is not necessarily a server side thing. Where are the downloaded files ing from? Browser's memory like in the linked question? Same-origin (Then you could pre-fetch them using AJAX and pack them in memory if server-side really is not an option)? Other domains? (In that case you'll still be unable to even donwload them from Firefox). – Kaiido Commented May 22, 2019 at 1:52
 |  Show 6 more ments

3 Answers 3

Reset to default 5

Instead of downloading multiple files, the best is probably to pack them all in a single file. You can for instance generate a zip file from all these files.

Since you stated that server-side solution was not an option for you, but that all your files are hosted on your domain, then I can propose you to first fetch through AJAX all your files (since these are images already loaded on the page, they won't even need to be actually downloaded again, but only gathered from cache) and then build your zip file on the front-end.

// all images are from wikimedia
const urls = ['/3/3b/Hulda_Klagers_house_and_lawn.jpg/320px-Hulda_Klagers_house_and_lawn.jpg', '/1/15/P%C3%A8re-Lachaise_-_Division_79_-_Floriot_02.jpg/320px-P%C3%A8re-Lachaise_-_Division_79_-_Floriot_02.jpg', '/a/a6/V37-20180910-055_%2845088120261%29.jpg/320px-V37-20180910-055_%2845088120261%29.jpg', '/2/2b/MormantulLuiAmzaPellea_%284%29.JPG/360px-MormantulLuiAmzaPellea_%284%29.JPG', '/f/f8/Launch_of_LAWRENCE_LCCN2014710971.tif/lossy-page1-174px-Launch_of_LAWRENCE_LCCN2014710971.tif.jpg']
.map((url) => 'https://upload.wikimedia/wikipedia/mons/thumb' + url);

fetchBlobs(urls)
  .then(pack)
  .then((zipFile) => dl.href = URL.createObjectURL(zipFile));

function fetchBlobs(urls) {
  return Promise.all(
    urls.map((url) =>
      fetch(url)
      .then((resp) => resp.blob())
      .then((blob) => {
        // store the file name
        blob.name = url.slice(url.lastIndexOf('/') + 1)
        return blob;
      })
    )
  );
}
function pack(blobs) {
  const zip = new JSZip();
  const folder = zip.folder('my_images');
  blobs.forEach((blob) => folder.file(blob.name, blob));
  return zip.generateAsync({type : "blob"});
}
<!-- using JSZip library https://stuk.github.io/jszip/ -->
<script src="https://cdnjs.cloudflare./ajax/libs/jszip/3.2.0/jszip.min.js"></script>
<a id="dl" download="images.zip">download</a>

Ok, this is my working solution of downloading files at once with setTimeout()

var fileUrls = [...];    

var tempLink = document.createElement('a');
document.body.appendChild(tempLink);

downloadMultipleFiles(fileUrls);

function downloadMultipleFiles(fileUrls) {
    setTimeout(function() {

        var fileIndex = fileUrls.length*1-1*1;
        var fileUrl = fileUrls[fileIndex];
        tempLink.setAttribute('href', fileUrl);
        tempLink.setAttribute('download', fileUrl.split('/')[fileUrl.split('/').length*1-1*1]);
        tempLink.click();

        if(fileIndex > -1) {
            fileUrls.splice(fileIndex, 1);
        }

        if(fileUrls.length > 0) {
            downloadMultipleFiles(fileUrls);
        } else {
            document.body.removeChild(tempLink);
        }

    }, 200); // if less than 200, not all files are downloaded in Firefox
}

And this my working solution of zipping files without server side using jszip mentioned by @Kaiido:

// prepare blobs with data of files when load a page
var fileUrls = [...];
var blobs = [];
for(i = 0; i < fileUrls.length; i++) {

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if(this.readyState == 4 && this.status == 200) {

            var filename = this.responseURL.split('/')[this.responseURL.split('/').length*1-1*1];
            var mimeType = this.getResponseHeader('Content-Type');
            blobs.push([filename, new Blob([this.response], {type: mimeType})]);
        }
    };
    xhttp.open('GET', fileUrls[i], true);
    xhttp.responseType = "arraybuffer";
    xhttp.send();
}

document.getElementsByClassName('.download_all_link')[0].addEventListener('click', function(){

    if(this.id != '') {
        var zip = new JSZip();
        var folder = zip.folder('subfolder');

        for(i = 0; i < blobs.length; i++) {
            folder.file(blobs[i][0], blobs[i][1]);
        }

        zip.generateAsync({type : 'blob'})
            .then(zip_blob => {
                download_all.href = URL.createObjectURL(zip_blob);
            });

        // as we don't know when zip is ready, 
        // we check link href every 500 ms by using recursive function with setTimeout()
        checkHref(this);
    }
});
}

function checkHref(thisLink) {
    setTimeout(function() {

        // when zip is ready we click by the link again to download zip
        if(~thisLink.href.indexOf('blob:')) {
            thisLink.download = 'myfiles.zip';
            thisLink.id = ''; // to prevent zipping again
            thisLink.click(); // to download zip
        } else {
            checkHref(thisLink);
        }
    }, 500);
}

chrome

You can use setTimeout to download multiple files. (over than 10)

<button onclick="downloadAll()">DownloadAll</button>
<script>
  function downloadBatch(aElems, batchSize, delay) {
    let index = 0
    const nextBatch = () => {
      const batch = aElems.slice(index, index + batchSize)
      batch.forEach(aWithDownload => {
        aWithDownload.click()
      })
      index += batchSize
      if (index < aElems.length) {
        setTimeout(nextBatch, delay)
      }
    }
    nextBatch()
  }

  function downloadAll() {
    const maxDownloadsPerBatch = 8
    const delayBetweenBatches = 1000
    const aArray = [...document.querySelectorAll('a[download]')]
    downloadBatch(aArray, maxDownloadsPerBatch, delayBetweenBatches)
  }
</script>

<!-- 
发布评论

评论列表(0)

  1. 暂无评论