最近在作一个
文件下载
的功能,这里把作的过程当中用的技术和坑简要总结下。
针对单文件的状况下,同源的文件,能够经过 < a> 标签的 download 属性下载文件前端
const elt = document.createElement('a'); elt.setAttribute('href', url); elt.setAttribute('download', 'file.png'); elt.style.display = 'none'; document.body.appendChild(elt); elt.click(); document.body.removeChild(elt);
可是这个方案并不适用于非同源
的资源,此时它至关于普通的超连接,点击会跳转到资源页面,而不是下载。canvas
若是不存在CORS问题, 能够借助Blob
实现下载(构造xhr请求文件地址, 以Blob的形式接收Response):后端
function downloadWithBlob(url) { fetch(url).then(res => res.blob().then(blob => { var a = document.createElement('a'); var url = window.URL.createObjectURL(blob); var filename = 'file.png'; a.href = url; a.download = filename; a.click(); window.URL.revokeObjectURL(url); })); }
若是存在CORS问题,能够考虑使用 canvas
将图片转换成 base64
编码以后再经过 标签的 download 属性下载浏览器
function downloadPic(url) { const img = new Image; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); img.onload = function() { canvas.width = this.width; canvas.height = this.height; ctx.drawImage(this, 0, 0); const elt = document.createElement('a'); elt.setAttribute('href', canvas.toDataURL('image/png')); elt.setAttribute('download', 'file.png'); elt.style.display = 'none'; document.body.appendChild(elt); elt.click(); document.body.removeChild(elt); }; img.crossOrigin = 'anonymous'; img.src = url; }
iframe
方式是在页面内隐藏iframe, 而后将下载地址加载到iframe中, 从而触发浏览器的下载行为app
const iframe = document.createElement('iframe'); iframe.src = url; iframe.style.display = 'none'; document.body.appendChild(iframe);
可是这里发现,即便是同域的图片,也没法完成下载,这是为啥呢?fetch
这里就有个上面的a连接下载没有提到的问题:什么样的连接才能触发浏览器的下载
:this
一个url可否触发浏览器自动下载,主要看该请求响应头response header
是否知足,通常是看Content-Disposition
和Content-Type
这两个消息头:编码
Content-Disposition为attachment
,它表示让浏览器把消息体以附件的形式下载并保存到本地 (通常还会指定filename, 下载的文件名默认就是filename)Content-Type 为 application/octet-stream
(无类型) 或 application/zip
(zip包时)等等。(其中 application/octet-stream表示http response为二进制流(没指定明确的type), 用在未知的应用程序文件,浏览器通常不会自动执行或询问执行。浏览器会像对待 设置了HTTP头Content-Disposition 值为 attachment 的文件同样来对待这类文件)只要url知足上述触发的要求,那么均可以经过iframe的形式来下载url
若是后端本身也能控制的话,或者后端能配合的话,能够写一个代理服务,在后端去请求文件数据,而后设置好相应的response header, 而后前端请求代理服务来作下载。代理
前端(假设代理服务接口是http://exampale.com/download):
const downloadUrl = 'http://exampale.com/download?url=' + encodeURIComponent(url) + '&name=xxx'; const elt = document.createElement('a'); elt.setAttribute('href', downloadUrl); elt.setAttribute('download', 'file.png'); ...
后端
const url = decodeURIComponent(req.query.url); http.get(url, (response) => { res.setHeader('Content-disposition', 'attachment;filename=' + req.query.name); res.setHeader('Content-type', 'application/octet-stream'); response.pipe(res); });
单文件的处理先写到这里,多文件的下载下篇在写。