问题
当使用iframe作为文件下载的载体时,如何知道文件已经下载完毕,现有的iframe的方法具有兼容性问题,在Chrome、IE下无法监听事件监听文件下载完毕,因为事件本身也是对iframe中的html结构的加载进度监听。onload
onload
onload
const url = 'http://www.example.com/file.zip'; const iframe = document.createElement('iframe'); iframe.src = url; iframe.style.display = 'none'; iframe.onload = function() { console.log('start downloading...'); document.body.removeAttribute(iframe); } document.body.appendChild(iframe);
当Chrome、IE下时,如果HTTP文件头中包含Content-disposition: attachment;即下载文件的链接的话,不会触发这个事件事件。onload
关于Content-disposition
Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。 Content-Disposition为属性名disposition-type是以什么方式下载,如attachment为以附件方式下载disposition-parm为默认保存时的文件名服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,当代码里面使用Content-Disposition来确保浏览器弹出下载对话框的时候。 ,一定要确保没有做过关于禁止浏览器缓存的操作。 代码如下: 不然会发现下载功能在Opera和FireFox里面好好的没问题,在IE下面就是不行。response.addHeader(‘Content-Disposition’, ‘attachment’);
response.setHeader(‘Pragma’, ‘No-cache’);response.setHeader(‘Cache-Control’, ‘No-cache’); response.setDateHeader(‘Expires’, 0);
解决1:利用Cookie
Nginx配置如下:
add_header Set-Cookie "fileDownloaded=1; path=/;" always;
后端将文件下载进度放在Cookie中,通过轮询Cookie的方式,对文件下载进度进行获取,判断文件是否已经下载完毕。
let link = document.createElement('iframe'); link.src = '/your/file/path/name.zip'; link.style.display = 'none'; document.body.appendChild(link); let timer = setInterval(() => { // 通过判断是否有这个cookie来确定是否下载完成 if (Cookies.get('fileDownloaded')) { console.log('下载完成啦'); clearInterval(timer); document.body.removeChild(link); Cookies.remove('fileDownloaded'); } }, 500);`
缺陷:
- 需要后端配合
- 如果客户端禁用了Cookie,则该方案完全失效;在无痕浏览模式下,读取Cookie,甚至代码报错。
解决2:轮询监听readyState
定时器轮询监听readyState的状态,如果是 complete 或者 interactive 说明文件加载完成。
let iframe = document.createElement('iframe'); iframe.src = path; iframe.style.display = 'none'; document.body.appendChild(iframe); const timer = setInterval(() => { const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; if (iframeDoc.readyState == 'complete' || iframeDoc.readyState == 'interactive') { document.body.removeAttribute(iframe); clearInterval(timer); resolve('success'); } }, 1000);
该种方法比较好,因为不需要后端进行配合,且不依赖与Cookie等变量带来的问题,且能实现我们的需求。