How Can I Let a User Download Multiple Files When a Button Is Clicked?
So I Have a Httpd Server Running Which Has Links to a Bunch of Files. Lets Say the User Selects Three Files from a File List to Download and They're Located...
So I have a httpd server running which has links to a bunch of files. Lets say the user selects three files from a file list to download and they're located at:
When they click the download button I want them to download these three files from the links above.
My download button looks something like:
var downloadButton = new Ext.Button({
text: "Download",
handler: function(){
//download the three files here
}
});
12 Answers
The best way to do this is to have your files zipped and link to that:
The other solution can be found here: How to make a link open multiple pages when clicked
Which states the following:
HTML:
<a href="#" class="yourlink">Download</a>
JS:
$('a.yourlink').click(function(e) {
e.preventDefault();
window.open(');
window.open(');
window.open(');
});
Having said this, I would still go with zipping the file, as this implementation requires JavaScript and can also sometimes be blocked as popups.
This was the method which worked best for me and didn't open up new tabs, but just downloaded the files/images I required:
var filesForDownload = [];
filesForDownload( { path: "/path/file1.txt", name: "file1.txt" } );
filesForDownload( { path: "/path/file2.jpg", name: "file2.jpg" } );
filesForDownload( { path: "/path/file3.png", name: "file3.png" } );
filesForDownload( { path: "/path/file4.txt", name: "file4.txt" } );
$jq('input.downloadAll').click( function( e )
{
e.preventDefault();
var temporaryDownloadLink = document.createElement("a");
temporaryDownloadLink.style.display = 'none';
document.body.appendChild( temporaryDownloadLink );
for( var n = 0; n < filesForDownload.length; n++ )
{
var download = filesForDownload[n];
temporaryDownloadLink.setAttribute( 'href', download.path );
temporaryDownloadLink.setAttribute( 'download', download.name );
temporaryDownloadLink.click();
}
document.body.removeChild( temporaryDownloadLink );
} );
I fond that executing click() event on a element inside a for loop for multiple files download works only for limited number of files (10 files in my case). The only reason that would explain this behavior that made sense to me, was speed/intervals of downloads executed by click() events.
I figure out that, if I slow down execution of click() event, then I will be able to downloads all files.
This is solution that worked for me.
var urls = [
'
'
'
]
var interval = setInterval(download, 300, urls);
function download(urls) {
var url = urls.pop();
var a = document.createElement("a");
a.setAttribute('href', url);
a.setAttribute('download', '');
a.setAttribute('target', '_blank');
a.click();
if (urls.length == 0) {
clearInterval(interval);
}
}
I execute download event click() every 300ms. When there is no more files to download urls.length == 0 then, I execute clearInterval on interval function to stop downloads.
You can either:
- Zip the selected files and return the one zipped file.
- Open multiple pop-ups, each prompting for a download.
Note: Option one is objectively better.
And an option three: Download multiple files with a single action
I've solved this a different way by using window.location. It works in Chrome, which fortunately is the only browser I had to support. Might be useful to someone. I'd initally used Dan's answer, which also needed the timeout I've used here or it only downloaded one file.
var linkArray = [];
linkArray.push("");
linkArray.push("");
linkArray.push("");
function (linkArray) {
for (var i = 0; i < linkArray.length; i++) {
setTimeout(function (path) { window.location = path; }, 200 + i * 200, linkArray[i]);
}
};
This works in all browsers (IE11, Firefox, Microsoft Edge, Chrome and Chrome Mobile) My documents are in multiple select elements. The browsers seem to have issues when you try to do it too fast... So I used a timeout.
<select class="document">
<option val="word.docx">some word document</option>
</select>
//user clicks a download button to download all selected documents
$('#downloadDocumentsButton').click(function () {
var interval = 1000;
//select elements have class name of "document"
$('.document').each(function (index, element) {
var doc = $(element).val();
if (doc) {
setTimeout(function () {
window.location = doc;
}, interval * (index + 1));
}
});
});
This solution uses promises:
function downloadDocs(docs) {
docs[0].then(function (result) {
if (result.web) {
window.open(result.doc);
}
else {
window.location = result.doc;
}
if (docs.length > 1) {
setTimeout(function () { return downloadDocs(docs.slice(1)); }, 2000);
}
});
}
$('#downloadDocumentsButton').click(function () {
var files = [];
$('.document').each(function (index, element) {
var doc = $(element).val();
var ext = doc.split('.')[doc.split('.').length - 1];
if (doc && $.inArray(ext, docTypes) > -1) {
files.unshift(Promise.resolve({ doc: doc, web: false }));
}
else if (doc && ($.inArray(ext, webTypes) > -1 || ext.includes('?'))) {
files.push(Promise.resolve({ doc: doc, web: true }));
}
});
downloadDocs(files);
});
You could go for the iframe.
Note: though this is one of the few ways to download multiple files at once, without creating pop-up, this won't work with files that can be rendered in browser. Check the JavaScript code comments.
Note 2: some browsers may ask for permission to download multiple files from the same page
function download(){
const links = ["", "", ""]
// It only works with files that don't render in the browser
// I.e., not video, not text, not and photo
for(let i = 0; i < links.length; i++) {
var frame = document.createElement("iframe");
frame.src = links[i];
frame["download"] = 1
document.body.appendChild(frame);
}
}
iframe{
display: none;
}
<body>
<div onclick="download()">Download</div>
</body>
Use:
<!DOCTYPE html>
<html ng-app='app'>
<head>
<title>
</title>
<link rel="stylesheet" href="">
<link rel="stylesheet" href="style.css">
</head>
<body ng-cloack>
<div class="container" ng-controller='FirstCtrl'>
<table class="table table-bordered table-downloads">
<thead>
<tr>
<th>Select</th>
<th>File name</th>
<th>Downloads</th>
</tr>
</thead>
<tbody>
<tr ng-repeat = 'tableData in tableDatas'>
<td>
<div class="checkbox">
<input type="checkbox" name="{{tableData.name}}" id="{{tableData.name}}" value="{{tableData.name}}" ng-model= 'tableData.checked' ng-change="selected()">
</div>
</td>
<td>{{tableData.fileName}}</td>
<td>
<a target="_self" id="download-{{tableData.name}}" ng-href="{{tableData.filePath}}" class="btn btn-success pull-right downloadable" download>download</a>
</td>
</tr>
</tbody>
</table>
<a class="btn btn-success pull-right" ng-click='downloadAll()'>download selected</a>
<p>{{selectedone}}</p>
</div>
<script src=""></script>
<script src=""></script>
<script src="script.js"></script>
</body>
</html>
File app.js
var app = angular.module('app', []);
app.controller('FirstCtrl', ['$scope','$http', '$filter', function($scope, $http, $filter){
$scope.tableDatas = [
{name: 'value1', fileName:'file1', filePath: 'data/file1.txt', selected: true},
{name: 'value2', fileName:'file2', filePath: 'data/file2.txt', selected: true},
{name: 'value3', fileName:'file3', filePath: 'data/file3.txt', selected: false},
{name: 'value4', fileName:'file4', filePath: 'data/file4.txt', selected: true},
{name: 'value5', fileName:'file5', filePath: 'data/file5.txt', selected: true},
{name: 'value6', fileName:'file6', filePath: 'data/file6.txt', selected: false},
];
$scope.application = [];
$scope.selected = function() {
$scope.application = $filter('filter')($scope.tableDatas, {
checked: true
});
}
$scope.downloadAll = function(){
$scope.selectedone = [];
angular.forEach($scope.application,function(val){
$scope.selectedone.push(val.name);
$scope.id = val.name;
angular.element('#'+val.name).closest('tr').find('.downloadable')[0].click();
});
}
}]);
Plunker example:
This is the easiest way I have found to download multiple files.
$('body').on('click','.download_btn',function(){
downloadFiles([
['File1.pdf', 'File1-link-here'],
['File2.pdf', 'File2-link-here'],
['File3.pdf', 'File3-link-here'],
['File4.pdf', 'File4-link-here']
]);
})
function downloadFiles(files){
if(files.length == 0){
return;
}
file = files.pop();
var Link = $('body').append('<a href="'+file[1]+'" download="file[0]"></a>');
Link[0].click();
Link.remove();
downloadFiles(files);
}
This should work for you.
downloadAll(links) {
const urls = links;
for (let link of urls) {
window.open(link, '_blank');
}
}
This code defines a downloadAll function that takes an array of links as a parameter. It then iterates over each link and opens it in a new browser tab using window.open(link, '_blank'). This will trigger the file download for each link.
However, please note that to successfully download files using this method, the user's browser settings must allow pop-up windows. If pop-up blocking is enabled, the files may not be downloaded as intended.
Keep in mind that this solution relies on the browser's default behavior for opening new tabs and triggering file downloads. Different browsers may handle this differently, and there's no guarantee that all files will be downloaded simultaneously. It's also worth noting that if you're trying to download a large number of files, it may cause performance issues or exceed browser limitations.
Another possibility is using the downloads.download() API. Which is however not supported by Safari.
await Promise.all(
browser.downloads.download({url : downloadUrl1}),
browser.downloads.download({url : downloadUrl2})
);
This solution works perfectly fine for me:
var downloadButton = new Ext.Button({
text: "Download",
handler: function () {
/** @type {Array<string>} URLS */
const URLS = [
" ",
"",
"",
];
for (let x = 0; x < URLS.length; x++) {
/** @type {string} URL */
const URL = URLS[x];
/** @type {HTMLLinkElement} LINK */
const LINK = document.createElement('a');
LINK.href = URL;
LINK.setAttribute('download', 'download');
document.body.appendChild(LINK);
LINK.click();
LINK.parentNode.removeChild(LINK);
window.URL.revokeObjectURL(URL);
}
}
});