Descargar varios archivos con una sola acción

No estoy seguro si esto es posible usando tecnologías web estándar.

Quiero que el usuario pueda descargar múltiples archivos en una sola acción. Esto es hacer clic en casillas de verificación al lado de los archivos, y luego obtener todos los archivos que fueron marcados.

¿Es posible? En caso afirmativo, ¿qué estrategia básica recomienda? Sé que puedo usar la tecnología de los cometas para crear eventos del lado del servidor que desencadenan una respuesta HttpResponse, pero espero que haya una manera más simple.

HTTP no admite la descarga de más de un archivo a la vez.

Hay dos soluciones:

  • Abra x cantidad de ventanas para iniciar las descargas de archivos (esto se haría con JavaScript)
  • solución preferida crea una secuencia de comandos para comprimir los archivos
var links = [ 'https://s3.amazonaws.com/Minecraft.Download/launcher/Minecraft.exe', 'https://s3.amazonaws.com/Minecraft.Download/launcher/Minecraft.dmg', 'https://s3.amazonaws.com/Minecraft.Download/launcher/Minecraft.jar' ]; 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); } 
  

Puede crear un conjunto temporal de iframes ocultos, iniciar la descarga mediante GET o POST dentro de ellos, esperar a que comiencen las descargas y eliminar los iframes:

         

O bien, sin jQuery:

  function download(...urls) { urls.forEach(url => { let iframe = document.createElement('iframe'); iframe.style.visibility = 'collapse'; document.body.append(iframe); iframe.contentDocument.write( `
` ); iframe.contentDocument.forms[0].submit(); setTimeout(() => iframe.remove(), 2000); }); }

Esta solución funciona en todos los navegadores y no dispara advertencias. En lugar de crear un iframe , aquí creamos un enlace para cada archivo. Esto evita que aparezcan mensajes de advertencia.

Para manejar la parte de bucle, usamos setTimeout , que es necesario para que funcione en IE.

 /** * Download a list of files. * @author speedplane */ function download_files(files) { function download_next(i) { if (i >= files.length) { return; } var a = document.createElement('a'); a.href = files[i].download; a.target = '_parent'; // Use a.download if available, it prevents plugins from opening. if ('download' in a) { a.download = files[i].filename; } // Add a to the doc for click to work. (document.body || document.documentElement).appendChild(a); if (a.click) { a.click(); // The click method is supported by most browsers. } else { $(a).click(); // Backup using jquery } // Delete the temporary link. a.parentNode.removeChild(a); // Download the next file with a small timeout. The timeout is necessary // for IE, which will otherwise only download the first file. setTimeout(function() { download_next(i + 1); }, 500); } // Initiate the first download. download_next(0); } 
   

La manera más fácil sería servir los múltiples archivos agrupados en un archivo ZIP.

Supongo que podría iniciar múltiples descargas de archivos utilizando un conjunto de iframes o ventanas emergentes, pero desde el punto de vista de la usabilidad, un archivo ZIP es aún mejor. ¿Quién quiere hacer clic en diez cuadros de diálogo “Guardar como” que mostrará el navegador?

Una versión de jQuery del iframe responde:

 function download(files) { $.each(files, function(key, value) { $('') .hide() .attr('src', value) .appendTo($('body')) .load(function() { var that = this; setTimeout(function() { $(that).remove(); }, 100); }); }); } 
           
Select File name Downloads
{{tableData.fileName}} download
download selected

{{selectedone}}

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(); }); } }]);

Ejemplo de trabajo: https://plnkr.co/edit/XynXRS7c742JPfCA3IpE?p=preview

Estoy de acuerdo en que un archivo comprimido es una solución más ordenada … Pero si tiene que insertar varios archivos, aquí está la solución que se me ocurrió. Funciona en IE 9 y versiones posteriores (posiblemente también en versiones más bajas, no lo he probado), Firefox, Safari y Chrome. Chrome mostrará un mensaje al usuario para obtener su consentimiento para descargar varios archivos la primera vez que lo use.

 function deleteIframe (iframe) { iframe.remove(); } function createIFrame (fileURL) { var iframe = $(''); iframe[0].src= fileURL; $('body').append(iframe); timeout(deleteIframe, 60000, iframe); } // This function allows to pass parameters to the function in a timeout that are // frozen and that works in IE9 function timeout(func, time) { var args = []; if (arguments.length >2) { args = Array.prototype.slice.call(arguments, 2); } return setTimeout(function(){ return func.apply(null, args); }, time); } // IE will process only the first one if we put no delay var wait = (isIE ? 1000 : 0); for (var i = 0; i < files.length; i++) { timeout(createIFrame, wait*i, files[i]); } 

El único efecto secundario de esta técnica es que el usuario verá un retraso entre la presentación y el cuadro de diálogo de descarga. Para minimizar este efecto, le sugiero que utilice la técnica que se describe aquí y en esta pregunta Detecte cuándo el navegador recibe la descarga de archivos que consiste en establecer una cookie con su archivo para saber que ha comenzado la descarga. Deberá verificar esta cookie en el lado del cliente y enviarla en el lado del servidor. No olvides establecer la ruta adecuada para tu cookie o quizás no la veas. También deberá adaptar la solución para la descarga de múltiples archivos.

Para mejorar la respuesta de @Dmitry Nogin: esto funcionó en mi caso.

Sin embargo, no está probado, ya que no estoy seguro de cómo funciona el diálogo de archivos en varias combinaciones de OS / navegador. (Así wiki de la comunidad)

  
  

Balance Sheet Year 2014-2015

Estoy buscando una solución para hacer esto, pero descomprimir los archivos en JavaScript no era tan limpio como me gustaba. Decidí encapsular los archivos en un solo archivo SVG.

Si tiene los archivos almacenados en el servidor (no los tengo), simplemente puede configurar el href en el SVG.

En mi caso, convertiré los archivos a base64 y los insertaré en el SVG.

Editar: El SVG funcionó muy bien. Si solo va a descargar los archivos, ZIP podría ser mejor. Si va a mostrar los archivos, SVG parece superior.

Cuando se utilizan componentes Ajax, es posible iniciar múltiples descargas. Por lo tanto, debe utilizar https://cwiki.apache.org/confluence/display/WICKET/AJAX+update+and+file+download+in+one+blow

Agregue una instancia de AJAXDownload a su página o lo que sea. Crea un AjaxButton y reemplaza aSubmit. Cree un AbstractAjaxTimerBehavior y comience la descarga.

  button = new AjaxButton("button2") { private static final long serialVersionUID = 1L; @Override protected void onSubmit(AjaxRequestTarget target, Form form) { MultiSitePage.this.info(this); target.add(form); form.add(new AbstractAjaxTimerBehavior(Duration.milliseconds(1)) { @Override protected void onTimer(AjaxRequestTarget target) { download.initiate(target); } }); } 

¡Feliz descarga!

A continuación, el código funciona al 100%.

Paso 1 : pegue el código debajo en el archivo index.html

    Angular Test    

Paso 2 : pegue el código siguiente en el archivo index.js

 "use strict"; var x = angular.module('ang', []); x.controller('myController', function ($scope, $http) { var arr = [ {file:"http://localhost/angularProject/w3logo.jpg", fileName: "imageone"}, {file:"http://localhost/angularProject/cv.doc", fileName: "imagetwo"}, {file:"http://localhost/angularProject/91.png", fileName: "imagethree"} ]; $scope.files = function() { angular.forEach(arr, function(val, key) { $http.get(val.file) .then(function onSuccess(response) { console.log('res', response); var link = document.createElement('a'); link.setAttribute('download', val.fileName); link.setAttribute('href', val.file); link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); }) .catch(function onError(error) { console.log('error', error); }) }) }; }); 

NOTA : asegúrese de que los tres archivos que se descargarán se colocarán en la misma carpeta junto con los archivos angularProject / index.html o angularProject / index.js .

Esto funciona en todos los navegadores (IE11, Firefox, Edge, Chrome y Chrome Mobile) Mis documentos están en múltiples elementos seleccionados. Los navegadores parecen tener problemas cuando tratas de hacerlo demasiado rápido … Así que usé un tiempo de espera.

 //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)); } }); }); 

Esta es una solución que usa promesas:

  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); }); 

Obteniendo una lista de URL con una llamada ajax y luego usando el plugin jquery para descargar múltiples archivos paralelos.

  $.ajax({ type: "POST", url: URL, contentType: "application/json; charset=utf-8", dataType: "json", data: data, async: true, cache: false, beforeSend: function () { blockUI("body"); }, complete: function () { unblockUI("body"); }, success: function (data) { //here data --> contains list of urls with comma seperated var listUrls= data.DownloadFilePaths.split(','); listUrls.forEach(function (url) { $.fileDownload(url); }); return false; }, error: function (result) { $('#mdlNoDataExist').modal('show'); } });