JavaScript实现多文件下载方法解析

作者:函数茶馆

时间:2026-10-23

浏览:592

本文详细解析了前端实现多文件下载的各种技术方案,涵盖HTML5 download属性、Blob流处理、dataURL转换及低版本浏览器兼容策略。通过封装lib.js提供单文件、多文件及自定义命名下载接口,并探讨后端服务器配置与跨协议传输的安全注意事项,帮助开发者在不同场景下选择最优下载方案。

文章目录

前端多文件下载需求与场景

文件下载是前端开发中极为常见的需求,广泛应用于HighChart统计图导出、在线图片编辑保存以及在线代码编辑器导出等场景。通常,前端仅提供一个链接,用户需右键点击并选择“另存为”才能保存文件。虽然操作不算复杂,但若需保存页面中的多个文件,用户必须重复多次操作,例如在英语听力网站下载多个音频时,这种繁琐的过程极易引发用户疲劳。

本文旨在介绍如何利用JavaScript实现多文件下载功能,即用户点击链接或按钮时,系统能同时下载多个文件。需要注意的是,“同时”一词在不同浏览器中表现不同:现代浏览器支持并行下载,而在IE8-等旧版浏览器中,由于不支持并行机制,只能实现串行下载,即多个文件依次保存。

若读者希望忽略具体实现细节,可直接跳转至第三部分,或访问相关代码仓库链接。

核心代码封装于lib.js文件中。

提供HTTPS协议的DEMO示例:javascript-multiple-download(注:第三个测试项存在Bug,具体原因将在后文说明)。

提供HTTP协议的DEMO示例:javascript-multiple-download(测试运行正常)。

文件类型及其特点分析

一、文件类型介绍及其特点

1. 一般类型

常见的文件格式包括txt、png、jpg、zip、tar等。部分浏览器会根据链接直接打开并显示内容,而另一部分浏览器若无法识别响应头或不能解析对应格式,则会直接触发下载行为。例如:


<a href="http://barretlee.com/test.rar" rel="external nofollow" >file</a>

上述代码中,若直接点击链接,浏览器将直接下载该文件。

2. dataURL类型

dataURL也是一种常见类型,可作为src或url()的参数使用。常见的格式包括:


文本: data:text/plain;这里是正文内容。
图片: data:image/jpg;base64,/9j/4AAQSkZJRgABAQEA....
  data:image/png;base64,/9j/4AAQSkZJRgABAQEA....

其中,base64是应用较为广泛的一种数据格式。


Base64格式
data:[][;charset=][;base64],

Base64 在CSS中的使用:
.demoImg{ background-image: url("data:image/jpg;base64,/9j/4QMZRXhpZgAASUkqAAgAAAAL...."); }

Base64 在HTML中的使用:
<img width="40" height="30" src="data:image/jpg;base64,/9j/4QMZRXhpZgAASUkqAAgAAAAL...." />  

3. Blob流

Blob对象表示不可变的、包含原始数据的类文件对象,具体用法可参考MDN文档。

其使用方式十分便捷,例如:


var aFileParts = ['<a id="a"><b id="b">hey!</b></a>'];
var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // the blob

Blob构造函数接收两个参数:第一个是数组类型的数据对象,可以是ArrayBuffer、ArrayBufferView、Blob或String等类型;第二个参数用于设置MIME类型。本文重点涉及URL.createObjectURL()函数,该函数将URL所代表的内容转化为一个DOMString,结果为一个文件对象或Blob对象。

4. 二进制流

利用File API读取文件时,获取的是数据的二进制流格式,这些类型可直接被ArrayBuffer等接收。本文未深入探讨此部分,故不作细述。

JavaScript多文件下载实现方案

二、JavaScript 多文件下载

HTML5中a标签新增了一个download属性。当链接包含该属性时,点击链接不会在浏览器中打开文件,而是直接触发下载。尽管该属性十分便利,但低版本浏览器并不兼容,下文第2和第3部分将介绍相应的解决方案。

在此,我们可以利用属性检测User Agent(UA)来判断浏览器类型:


h5Down = document.createElement("a").hasOwnProperty("download");
var h5Down = !/Trident|MSIE/.test(navigator.userAgent); // Trident 标识 IE11

1. a 标签 download 属性的使用

注意:FF5.0、Safari5.0、Opera11.1、IE9.0均不支持download属性。

利用download属性可直接下载单个文件。若需点击一次下载多个文件,则需进行额外处理:


function downloadFile(fileName, content){
 var aLink = document.createElement("a"),
  evt = document.createEvent("HTMLEvents");

 evt.initEvent("click");
 aLink.download = fileName;
 aLink.href = content;

 aLink.dispatchEvent(evt);
}

download属性的作用除了让浏览器忽略文件的MIME类型外,还会将该属性的值作为文件名。你可以在Chrome控制台运行以下代码:


downloadFile("barretlee.html", "./");

浏览器会提示是否保留(下载)该html文件。此前我们提到文件类型可能为dataURL或Blob流,为使程序支持这些数据类型,对上述函数稍作修改:


function downloadFile(fileName, content){
 var aLink = document.createElement('a');
  , blob = new Blob([content])
  , evt = document.createEvent("HTMLEvents");

 evt.initEvent("click");

 aLink.download = fileName;
 aLink.href = URL.createObjectURL(blob);
 aLink.dispatchEvent(evt);
}

通过new Blob([content])将文件转换为Blob流,再使用URL.createObjectURL()将其转换为DOMString。如此,即可支持data64及其他数据类型的content内容。

2. window.open 之后 execCommand("SaveAs")

前文已提及,尽管download属性是HTML5的便利工具,但低版本IE浏览器并不支持。针对IE,仍有其他转换方式,例如通过ADOBE.STREAM的activeX对象将文件转换为文件流,再写入保存文件。这里介绍一种相对简便的方法:先将内容写入新开的window对象,然后利用execCommand执行保存命令,相当于在页面上按下Ctrl+S,从而将页面内的信息全部下载下来。


// 将文件在一个 window 窗口中打开,并隐藏这个窗口。
var win = window.open("path/to/file.ext", "new Window", "width=0,height=0");
// 在 win 窗口中按下 ctrl+s 保存窗口内容
win.document.execCommand("SaveAs", true, "filename.ext");
// 使用完了,关闭窗口
win.close();

该过程十分直观,但存在浏览器兼容性问题,而非代码问题。若使用搜狗浏览器或360浏览器打开新窗口,它们会新开一个标签页而非独立窗口。更棘手的是,部分浏览器会拦截window.open的窗口(可通过设置解决)。因此,需另寻他法。

3. iframe 中操作

既然新开窗口较为繁琐,我们便尝试在当前窗口内完成操作。


function IEdownloadFile(fileName, contentOrPath){
 var ifr = document.createElement('iframe');

 ifr.style.display = 'none';
 ifr.src = contentOrPath;

 document.body.appendChild(ifr);


 // 保存页面 -> 保存文件
 ifr.contentWindow.document.execCommand('SaveAs', false, fileName);
 document.body.removeChild(ifr);
}

对于一般链接,可直接为iframe添加src属性,然后执行saveAs命令。若使用的是data64编码的文件,又该如何处理?


var isImg = contentOrPath.slice(0, 10) === "data:image";

// dataURL 的情况
isImg && ifr.contentWindow.document.write("<img src='" + 
  contentOrPath + "' />");

处理方案较为简单:直接将文件写入iframe,然后执行保存操作。

代码封装与接口调用

三、代码的封装与接口介绍

1. 代码的封装以及相关 DEMO

封装代码位于lib.js。

DEMO示例:javascript-multiple-download(HTTPS,第三个测试项有Bug)。

javascript-multiple-download(HTTP,测试正常)。

Bug说明:经过细节处理后,代码基本兼容各浏览器。我将代码托管于https://raw.github.com,可能因HTTPS传输导致第三个测试项报错,报错内容为:HTTPS安全受到http://rawgithub.com/barretlee/javascript-multiple-download/master/file/test.jpg的威胁,而test.txt文件未报错。在HTTP协议下测试运行结果正常。(此处未深入探究,肯定存在深层安全原因,难道仅因它是jpg图片格式?感谢@屈屈提醒,跨协议传输存在安全问题。)后续Demo置于BAE上,运行无问题,但未测试Safari和Opera。

2. 接口的调用

提供了三个接口,分别支持单文件下载、多文件下载以及多文件下载自定义命名。

1)单文件下载


Downer("./file/test.txt");

2)多文件下载


Downer(["./file/test.txt","./file/test.txt"]);

3)多文件下载自定义命名


Downer({
 "1.txt":"./file/test.txt",
 "2.jpg":"./file/test.jpg"
}); 

文件的URL如./file/test.jpg均可替换为base64或其他格式,例如:


Downer({
  //这是一个很长的 dataURI,我用负的text-indent隐藏了,可直接复制
 "data64.jpg" : "data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAYADsDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9NgKKK8w8beO/EMVzGdJ08W2jW2t2WnXGpLdx+e7PPEkii3eJgYj5mwsJFkzkquAC3VFc0lFf10/UOlz1CivO9H+LEmreP7jw8mlBrLfPDa6nC1y0cksX30Z2t1hGCHBEcsjAqQVHOJPCfjnxFc+HNU1rxPpmh6Vp9mt232i01WWXmGV0IdXt0CrhD8+45xnaM4A00uZ9rgtZcq3vb5noFLXCfDz4j3vjbT9XM+hSabqdhtItWFxGswZCybTc28DjJDAkx7fRjzi/qmv6zH8LdR1m6sP+Ef12PSprprPzkufssyxMwXeBtfBA5xg0OLi2n/Vwh+8aUep1gFLXGeKhqmj+FtGmt9fvvtVvfWMc9w0VsWvUkuI43WUeVtAIcnMYQggYI6Hs6lqwk7pPuNrkPEHwn8N+KNRkvdQh1BpZJY7ho7fVru3haWPbslMUcqpvXYhD7d2VU54FFFNNxd1uPyJIfhb4dt/EkOuxwXy6hBNJPD/xNLryImkz5myHzPLUNkllC4J5IyAaWP4W+GY7+/uzp7yy3sc8UiTXc0kUazHdMIo2cpDvPLeWFyeTRRRd9wH6B8NNB8NyapJZx38kmpxrFePe6pdXbTKAQMmaRjkAkZHOMDOAKmbwLp9n8Pp/CGkr/Zmm/wBnyadbjLS+QjIUB+ZstjPdsn1oopOTfUa91prdf1+hFrfhnWNb0HSLCTVrGOaC6tbi+mXT323CwyrJtiXzv3RZkXljJgZ4PWuooopNtkpWVkf/2Q==" 
});

此处仅实现了Chrome兼容,IE下未做处理,因该需求较为少见。

服务器支持与后端配置

四、服务器支持与后端实现

1. 后端实现

若不使用前端,直接由后端实现的原理是在响应头中加入特殊标记。例如前端发送如下请求:


function download(path) {
 var ifrm = document.getElementById(frame);
 ifrm.src = "download.php?path="+path;
}

后端响应为:


<?php 
 header("Content-Type: application/octet-stream");
 header("Content-Disposition: attachment; filename=".$_GET['path']);
 readfile($_GET['path']);
?>

这告诉浏览器这是一个流文件,作为附件发送,请忽略MIME type,直接保存。

2. 服务器配置

若后台使用Apache作为服务器,可配置htaccess文件:


<filesmatch "\.(zip|rar)$"="">
Header set Content-Disposition attachment
</filesmatch>

意思是只要请求的是zip或rar类型的文件,就添加Content-Disposition:attachment的响应头。这样可在PHP代码中省略繁琐操作。

总结与参考资料

五、小结

由于行文仓促,文中难免存在错误。若对多文件下载有更好的提议,欢迎提出以便共同分享!

实现多文件下载的方式不止上述几种,且本文封装的代码未在FF、Safari、Opera中实现,因它们尚未兼容download属性,具体情况可查阅caniuse。建议在项目中将此类任务交由后端处理,仅需几句代码即可搞定。

六、参考资料

在浏览器端用JS创建和下载文件 AlloyTeam

Starting file download with Javascript Ahzaz's Blog

Blob 流 MDN

到此这篇关于JavaScript实现多文件下载方法解析的文章就介绍到这了,更多相关JavaScript多文件下载内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章