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
3 Answers
Reset to default 5Instead 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>
<!--