JavaScript多⽂件下载
对于⽂件的下载,可以说是⼀个⼗分常见的话题,前端的很多项⽬中都会有这样的需求,⽐如 highChart 统计图的导出,在线图⽚编辑中的图⽚保存,在线代码编辑的代码导出等等。⽽很多时候,我们只给了⼀个链接,⽤户需要右键点击链接,然后选择“另存为”,这个过程虽说不⿇烦,但还是需要两步操作,倘若⽤户想保存页⾯中的多个链接⽂件,就得重复操作很多次,最常见的就是英语听⼒⽹站上的⾳频下载,⼿都要点⿇!
本⽂的⽬的是介绍如何利⽤ javascript 进⾏多⽂件的下载,也就是当⽤户点击某个链接或者按钮的时候,同时下载多个⽂件。这⾥的“同时”⽤的不是很准确,在现代浏览器中可以实现多⽂件的并⾏下载,⽽在⼀些⽼版本浏览器,如IE8-,此类的浏览器就只能进⾏单个⽂件的下载,但是我们可以让多个⽂件依次保存下来,算是串⾏下载吧~
若要要⽆视实现细节,可以直接跳到第三部分,或者戳:
代码封装:
DEMO: (HTTPS,第三个有bug,具体原因下⾯有说明)
(HTTP,测试正常)
⼀、⽂件类型介绍及其特点
1. ⼀般类型
平时⽐较常见的有 txt、png、jpg、zip、tar 等各种⽂件格式,这些⽂件格式中,⼀部分浏览器是会直接打开链接显⽰内容的,⽽另外⼀部分,浏览器不识别响应头,或者不能解析对应的格式,于是当做⽂件直接下载下来了。如:
<a href="barretlee/test.rar">file</a>
这句代码,若直接点开链接,浏览器将会直接下载该⽂件。
2. dataURL类型
dataURL 也是⼗分常见的类型,他可以作为 src 或者 url() 的参数送进去。⽐较常见的有如下⼏种:
⽂本: data:text/plain;这⾥是正⽂内容。
图⽚: data:image/jpg;base64,/
data:image/png;base64,/
base64 是⽤的⽐较⼴泛的⼀种数据格式。
Base64格式
data:[][;charset=][;base64],
Base64 在CSS中的使⽤:
.demoImg{ background-image: url("data:image/jpg;base64,/"); }
Base64 在HTML中的使⽤:
<img width="40" height="30" src="data:image/jpg;base64,/"/>
3. Blob 流
Blob 对象表⽰不可变的、包含原始数据的类⽂件对象。具体的内容可以参阅。
他的使⽤也是特别的⽅便,如:
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 所代表的内容转化成⼀个,产⽣的结果是⼀个⽂件对象或者 Blob 对象。
4. ⼆进制流
我们利⽤ File API 读取⽂件的时候,拿到的是数据的⼆进制流格式,这些类型可以直接被 ArrayBuffer 等接收,本⽂中没有⽤到,就不细说了。
⼆、JavaScript 多⽂件下载
HTML5 中 a 标签多了⼀个属性——download,⽤户点击链接浏览器会打开并显⽰该链接的内容,若在链接中加了 download 属性,点击该链接不会打开这个⽂件,⽽是直接下载。虽说是⽐较好⽤,但低版本浏览器不兼容,这个在本节的 2 和 3 中将会讲到解决⽅案。
在这⾥,我们可以利⽤属性检测 UA 来判断浏览器类型:
h5Down = ateElement("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 = ateElement("a"),
evt = ateEvent("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 = ateElement('a');
, blob = new Blob([content])
, evt = ateEvent("HTMLEvents");
evt.initEvent("click");
aLink.download = fileName;
aLink.href = ateObjectURL(blob);
aLink.dispatchEvent(evt);
}
new Blob([content]),现将⽂件转换成⼀个 Blog 流,然后,使⽤ateObjectURL()将其转换成⼀个 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/", "new Window", "width=0,height=0");
// 在 win 窗⼝中按下 ctrl+s 保存窗⼝内容
Command("SaveAs", true, "");
// 使⽤完了,关闭窗⼝
win.close();
这个过程⼗分明了,不过这⾥会存在⼀个问题,并不是程序的问题,⽽是浏览器的问题,如果我们⽤搜狗浏览器或者 360浏览器打开新窗⼝的话,他会新开⼀个标签页,⽽不是新开⼀个窗⼝,更可恶的是部分浏览器拦截 window.open 的窗⼝(这个可以设置)。所以只好另觅他法了。
3. iframe 中操作
既然新开⼀个窗⼝那么⿇烦,我就在本窗⼝下完成⼯作~
function IEdownloadFile(fileName, contentOrPath){
var ifr = ateElement('iframe');
ifr.style.display = 'none';
ifr.src = contentOrPath;
document.body.appendChild(ifr);
// 保存页⾯ -> 保存⽂件
veChild(ifr);
}
⼀般的链接我们可以直接给 iframe 添加 src 属性,然后执⾏ saveAs 命令,倘若我们使⽤的是 data64 编码的⽂件,这个怎么办?var isImg = contentOrPath.slice(0, 10) === "data:image";
// dataURL 的情况
isImg && tWindow.document.write("<img src='" +
contentOrPath + "' />");
这个也⽐较好处理,直接把⽂件写⼊到 iframe 中,然后在执⾏保存。
三、代码的封装与接⼝介绍
1. 代码的封装以及相关 DEMO
封装:
DEMO: (HTTPS,第三个有bug)
(HTTP,测试正常)
2. 接⼝的调⽤
提供了三个接⼝,⽀持单⽂件下载,多⽂件下载,多⽂件下载⾃定义命名。
1)单⽂件下载
Downer("./");
2)多⽂件下载
Downer(["./","./"]);
3)多⽂件下载⾃定义命名
Downer({
"1.txt":"./",
"2.jpg":"./file/test.jpg"
});
⽂件的 URL 如./file/test.jpg都可以改成 base64 或者其他格式,如:
Downer({
//这是⼀个很长的 dataURI,我⽤负的text-indent隐藏了,可直接复制
"data64.jpg" : "data:image/jpg;base64,/9j/4AAQ WuooopNtkpWVkf/2Q=="
});
这⾥只做到了 chrome 兼容,IE 下懒得去看了,这个需求很少见!
四、服务器⽀持与后端实现
1. 后端实现
不使⽤前端,直接后端实现的原理,就是在响应头中加⼊⼀些特殊的标记,如前端发送这样的请求:
function download(path) {
var ifrm = ElementById(frame);
ifrm.src = "download.php?path="+path;
}
后端的响应为
<?php
header("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 属性,具体情况可以查看。建议在项⽬中把这样的事情交给后端,⼏句代码可以搞定。
六、参考资料
AlloyTeam
Ahzaz's Blog
MDN
>如何下载javascript
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论