【问题标题】:How can I let a user download multiple files when a button is clicked?单击按钮时,如何让用户下载多个文件?
【发布时间】:2013-08-29 09:34:33
【问题描述】:

所以我有一个 httpd 服务器正在运行,它有一堆文件的链接。假设用户从文件列表中选择三个文件进行下载,它们位于:

mysite.com/file1 
mysite.com/file2
mysite.com/file3

当他们点击下载按钮时,我希望他们从上面的链接下载这三个文件。

我的下载按钮看起来像:

var downloadButton = new Ext.Button({
  text: "Download",
  handler: function(){
    //download the three files here
  }
});

【问题讨论】:

  • 如果你在手机上,zip 很糟糕。您只需触发所有三个下载,chrome 甚至可以识别这一点并提示使用,如果他们想允许您的网站“下载多个文件”。如果你想使用 zip,你可以在文件中进行 ajax,使用 jszip 压缩它们,然后从生成的 JS 树对象中下载 zip 文件。
  • 触发下载的最佳方式是什么? window.open("mysite.com/file1")?
  • 我喜欢在可用时使用 js 方法(例如 danml.com/js/download.js),或者 save,这需要数据网址。不过我通常不下载文件,更多的字符串是由 JS 生成的,所以请接受我的建议
  • 一个 Chrome ONLY API,File System Access API,是新添加的用于多文件(流)下载。
  • 这能回答你的问题吗? Download multiple files with a single action

标签: javascript html apache extjs download


【解决方案1】:

这是我找到的下载多个文件的最简单方法。

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

这应该适合你。 谢谢。

【讨论】:

    【解决方案2】:
    //It is possible when using Tampermonkey (Firefox or Chrome).
    //They added the GM_Download command.
    //You can use it like this download multiple files One time:
    
    
    // ==UserScript==
    // @name        
    // @description 
    // @match       
    // @grant       
    // @grant       GM_download
    function setup_reader(file) {
        var name = file.name;
        var reader = new FileReader();
        reader.onload = function (e) {
            var bin = e.target.result; //get file content
            var lines = bin.split('\n');
            for (var line = 0; line < lines.length; line++) {
                console.log(lines[line]);
                GM_download(lines[line], line + '.jpg');
            }
        }
        // reader.readAsBinaryString(file);
        reader.readAsText(file, 'utf-8');//
    }
    
    

    【讨论】:

      【解决方案3】:

      我喜欢在for loop 内的a 元素上执行click() 事件以下载多个文件仅适用于有限数量的文件(在我的情况下为10 个文件)。解释这种对我有意义的行为的唯一原因是click() 事件执行的下载速度/间隔。

      我发现,如果我减慢click() 事件的执行速度,那么我将能够下载所有文件。

      这是对我有用的解决方案。

      var urls = [
        'http://example.com/file1',
        'http://example.com/file2',
        'http://example.com/file3'
      ]
      
      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);
        }
      }
      

      我每 300 毫秒执行一次下载事件click()。当urls.length == 0 没有更多文件要下载时,我在interval 函数上执行clearInterval 以停止下载。

      【讨论】:

      • 我知道按时间间隔执行代码应该是可以避免的,但有时你必须做你必须做的事情。
      • 我认为 10 是您浏览器中配置的最大连接数。
      • @CodeWriter23 可能是这样。您能否提供任何可以在 Chrome 和 Firefox 中设置 max number of connections 的链接?
      • Lukasz:不受程序控制。我最好的答案是 Chrome 有硬编码的限制,而 Firefox 它在 about:config 下 network.http.max-persistent-connections-per-server
      【解决方案4】:

      这适用于所有浏览器(IE11、Firefox、Microsoft Edge、Chrome 和 Chrome Mobile)我的文档位于多个选择元素中。当您尝试太快时,浏览器似乎有问题......所以我使用了超时。

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

      此解决方案使用承诺:

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

      【讨论】:

        【解决方案5】:
            <!DOCTYPE html>
            <html ng-app='app'>
                <head>
                    <title>
                    </title>
                    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
                    <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="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
                    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
                    <script src="script.js"></script>
                </body>
            </html>
        
        
        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 示例:https://plnkr.co/edit/XynXRS7c742JPfCA3IpE?p=preview

        【讨论】:

          【解决方案6】:

          我通过使用 window.location 以不同的方式解决了这个问题。它可以在 Chrome 中运行,幸运的是,它是我必须支持的唯一浏览器。可能对某人有用。我最初使用了 Dan 的答案,这也需要我在这里使用的超时时间,或者它只下载了一个文件。

          var linkArray = [];
          linkArray.push("http://example.com/downloadablefile1");
          linkArray.push("http://example.com/downloadablefile2");
          linkArray.push("http://example.com/downloadablefile3");    
          
          function (linkArray) {
            for (var i = 0; i < linkArray.length; i++) { 
              setTimeout(function (path) { window.location = path; }, 200 + i * 200, linkArray[i]);
            }        
          };
          

          【讨论】:

          • 这也适用于 Firefox,据报道也适用于 Edge,但我对自己进行测试没有兴趣(Edge 正在迁移到 Blink 自己——很好摆脱)。
          【解决方案7】:

          这是最适合我的方法,没有打开新标签,而是下载了我需要的文件/图像:

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

          【讨论】:

          • 我一直在使用这段代码@Dan,我在 chrome 中遇到了问题,它忽略了快速点击,所以不得不设置一个超时。
          • @IainMNorman 有趣的问题,超时似乎是一个足够好的解决方案,不知道如何解决它
          • 仅供阅读的其他人参考:下载属性不适用于跨域请求(至少对于最新版本的 Chrome)。我认为它曾经是但被修补了
          • temporaryDownloadLink.setAttribute('href', 'data:application/octet-stream,' + encodeURIComponent( download.path)); 需要强制浏览器下载图片,而不是在新标签页中打开。
          【解决方案8】:

          执行此操作的最佳方法是将文件压缩并链接到该文件:

          其他解决方案可以在这里找到:How to make a link open multiple pages when clicked

          声明如下:

          HTML:

          <a href="#" class="yourlink">Download</a>
          

          JS:

          $('a.yourlink').click(function(e) {
              e.preventDefault();
              window.open('mysite.com/file1');
              window.open('mysite.com/file2');
              window.open('mysite.com/file3');
          });
          

          话虽如此,我还是会压缩文件,因为这个实现需要 JavaScript,有时也可以作为弹出窗口被阻止。

          【讨论】:

          • 所以我无法访问 preventDefault() 函数,无论如何我可以等待用户在打开第二个文件之前对第一个文件执行操作吗?发生的事情是它直接进入第三个文件,我无法下载前两个。
          • 你在说什么动作?因为我不确定你能不能追踪到。
          • 我的电话是 window.open('mysite.com/file1', '_parent');它覆盖了所有其他窗口(这不起作用,因为我需要等待每个窗口上的用户交互)。我之所以使用_self是因为我不想切换窗口,我想留在父窗口上。
          • 然后最好创建 2 个页面:mysite.com/file1.html 和 mysite.com/file2.html。每次加载后,您都可以开始该页面的文件下载。并且需要用户交互。验证交互后,加载下一页。 (我不确定我的解释是否正确)
          【解决方案9】:

          您可以:

          1. 压缩所选文件并返回一个压缩文件。
          2. 打开多个弹出窗口,每个都提示下载。

          注意 - 选项一客观上更好。

          编辑 找到了选项三:https://stackoverflow.com/a/9425731/1803682

          【讨论】:

          • 是选项 3!
          猜你喜欢
          • 1970-01-01
          • 2019-10-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-16
          • 2011-02-07
          • 1970-01-01
          • 2022-11-01
          相关资源
          最近更新 更多