对于文件的下载,可以说是一个十分常见的话题,前端的很多项目中都会有这样的需求,比如 highChart 统计图的导出,在线图片编辑中的图片保存,在线代码编辑的代码导出等等。而很多时候,我们只给了一个链接,用户需要右键点击链接,然后选择“另存为”,这个过程虽说不麻烦,但还是需要两步操作,倘若用户想保存页面中的多个链接文件,就得重复操作很多次,最常见的就是英语听力网站上的音频下载,手都要点麻!

本文的目的是介绍如何利用 javascript 进行多文件的下载,也就是当用户点击某个链接或者按钮的时候,同时下载多个文件。这里的“同时”用的不是很准确,在现代浏览器中可以实现多文件的并行下载,而在一些老版本浏览器,如IE8-,此类的浏览器就只能进行单个文件的下载,但是我们可以让多个文件依次保存下来,算是串行下载吧~

若要要无视实现细节,可以直接跳到第三部分,或者戳:

代码封装:lib.js

DEMO:javascript-multiple-download (HTTPS,第三个有bug,具体原因下面有说明)

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

本文地址:http://www.cnblogs.com/hustskyking/p/multiple-download-with-javascript.html,转载请保留原文地址。

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

1. 一般类型

平时比较常见的有 txt、png、jpg、zip、tar 等各种文件格式,这些文件格式中,一部分浏览器是会直接打开链接显示内容的,而另外一部分,浏览器不识别响应头,或者不能解析对应的格式,于是当做文件直接下载下来了。如:

<ahref="http://barretlee.com/test.rar">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中的使用:<imgwidth="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 等诸多类型;第二个参数是 MINE 类型设置。而本文我们要用到的是 URLcreateObjectURL() 这个函数,他的作用是将一个 URL 所代表的内容转化成一个 DOMString,产生的结果是一个 文件对象 或者 Blob 对象。

4. 二进制流

我们利用 File API 读取文件的时候,拿到的是数据的二进制流格式,这些类型可以直接被 ArrayBuffer 等接收,本文中没有用到,就不细说了。

二、JavaScript 多文件下载

HTML5 中 a 标签多了一个属性——download,用户点击链接浏览器会打开并显示该链接的内容,若在链接中加了 download 属性,点击该链接不会打开这个文件,而是直接下载。虽说是比较好用,但低版本浏览器不兼容,这个在本节的 2 和 3 中将会讲到解决方案。

在这里,我们可以利用 属性检测 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 属性可以直接下载单个文件,若想点击一次下载多个文件,就得稍加处理下了:

functiondownloadFile(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 流,为了让程序也支持这些数据类型,稍微修改下上面的函数:

functiondownloadFile(fileName, content){var aLink = document.createElement('a');, blob= newBlob([content]), evt= document.createEvent("HTMLEvents");evt.initEvent("click");aLink.download=fileName;aLink.href=URL.createObjectURL(blob);aLink.dispatchEvent(evt);
}

new Blob([content]),现将文件转换成一个 Blog 流,然后,使用URL.createObjectURL() 将其转换成一个 DOMString。这样我们就支持 data64 和其他数据类型的 content 了~

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

上面也提到了,尽管 download 属性是十分便利的 H5 利器,但低版本 IE 根本不赏脸,要说方法,IE 还是有很多方式去转换的,比如 ADOBE.STREAM 的 activeX 对象可以把文件转换成文件流,然后写入到一个要保存的文件中。这里要谈到的是略微方便一点的方式:先把内容写到一个新开的 window 对象中,然后利用 execCommand 执行保存命令,就相当于我们在页面上按下 Ctrl+S,这样页面内的信息都会 down 下来。

//将文件在一个 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 中操作

既然新开一个窗口那么麻烦,我就在本窗口下完成工作~

functionIEdownloadFile(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. 后端实现

不使用前端,直接后端实现的原理,就是在响应头中加入一些特殊的标记,如前端发送这样的请求:

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

后端的响应为

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

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

2. 服务器配置

若后台是 apche 作为服务器,可以配置 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
本文转自Barret Lee博客园博客,原文链接:http://www.cnblogs.com/hustskyking/p/multiple-download-with-javascript.html,如需转载请自行联系原作者

JavaScript多文件下载相关推荐

  1. javascript 大文件下载,分片下载,断点续传

    javascript 大文件下载,分片下载,断点续传 文章目录 javascript 大文件下载,分片下载,断点续传 1:获取文件大小: 2:切片下载 3:合并数据 4:下载到本地 5:成功 6:完整 ...

  2. JavaScript 实现文件下载并重命名

    JavaScript 实现文件下载并重命名 第一种是HTML官网中的方法 <a href="/images/liang.jpg" download="文件名称&qu ...

  3. JavaScript下载文件下载 [Blob]

    HttpServletResponse下载压缩文件流报错,压缩文件错误或不可预料的压缩文件末端 错误可能 (0)如果用Blob处理response的流,则ajax请求中需要填写responseType ...

  4. asp.net返回值当文件下载问题

    mvc中返回的javascript当做文件下载 解决方案 Response.ContentEncoding = System.Text.Encoding.UTF8;   Response.Conten ...

  5. JavaScript (mp3、mp4、jpg、doc、txt、rar)单个、多文件批量下载

    JavaScript 多文件下载 HTML5中 a 标签新增了一个属性 download,一般情况下,用户点击a链接浏览器会打开对应的链接地址,如果链接地址是一个文件如(xxx.rar.xxx.jpg ...

  6. vue下载文件并重命名

    vue的项目,需要做一个文件上传下载的功能.上传是OK的,然鹅因为上传到minio的时候,使用了随机数命名,导致文件名称就变成了一大串字符.被客户驳回来了.那好吧,就加一个重命名功能好了. 不过这个项 ...

  7. Switch语句用法及案例

    ​ 一.Switch语句用法 switch是多分支语句,用于判断一个表达式的值,然后执行相应的语句.(可以实现多选一) switch语句执行思路:利用表达式的值,来判断执行哪个语句.(简单的来说就是利 ...

  8. HTML和JavaScript的D3文件放置位置和文件下载

    今天,接着补齐发文任务,我也是一个半黑不白的程序员了,不过,为照顾刚刚接触D3的小白朋友们,我把D3文件位置公布一下,如下图: 我是将D3文件放在一个文件夹下,所以,导入D3代码如下图: D3的文件下 ...

  9. excel文件下载下来损坏 js_使用Java / javascript和apache POI导出.xls文件时获取损坏的文件...

    我正在尝试从Web应用程序下载浏览器中的.xls文件.下面是相同的代码. try(FileInputStream inputStream = new FileInputStream("C:\ ...

最新文章

  1. oracle取两个小时内的数据,【求解】一个时间条件,查两个不同时间段数据怎么查...
  2. Vue组件之全局组件与局部组件
  3. cmd控制屏幕光标_电脑控制手机?上班时间愉快尽情地玩手机吧!它值得您拥有!...
  4. ubuntu10.04+hadoop0.20.2平台配置(完全分布式模式)
  5. js遍历对象的几种方法
  6. mysql查询包含字符串的记录_MySQL查询字符串中包含字符的记录
  7. 苹果回应iPhone邮件漏洞;GitHub连续3天爆严重宕机情况;腾讯开源Transformer推理加速工具| 极客头条...
  8. 物联网卡有哪几种类型
  9. centos 修改root密码_Ubuntu、Redhat、Kali、CentOS重置root密码方法汇总
  10. Android-创建简单登陆界面
  11. 自娱自乐的小题目(6)
  12. python xlwt_python xlwt模块简介
  13. 详解线上线下收单业务(一)第三方支付
  14. codeblocks编译运行后出现无效的16位应用程序,不兼容64位windows
  15. 【CSS】773- 《CSS揭秘》使用技巧总结(干货)
  16. 搞IT的you知道这些么……
  17. 手游联运新增开放平台!
  18. 宁德时代与蔚来签署全面战略合作协议;中国通信服务委任闫栋为公司总裁 | 美通企业日报...
  19. 怎么去面试测试工程师?
  20. predict函数在回归分析中的应用

热门文章

  1. python 判断图片是否损坏_检查图片是否损坏、图片后缀是否与实际图片类型对应 - Python...
  2. 2d与2.5d坐标转换_Three.js 地理坐标和三维空间坐标的转换
  3. mysql主从切换gtid不一致_解决mysql使用GTID主从复制错误问题
  4. python列表、元组、字典和集合的算法时间_27.Python列表(list)、元组(tuple)、字典(dict)和集合(set)详解...
  5. 笔记-中项案例题-2017年上-计算题
  6. Android中获取WebView加载的html中console.log输出的内容
  7. Android中使用Notification在通知栏中显示通知
  8. ElementUI中el-form实现表单重置以及将方法抽出为全局方法
  9. SpringBoot+Jquery+Jstree实现企业架构管理
  10. SpringBoot+Jquery实现前后端数据交互