filesaver源码解读
网页实现文件下载可以使用成熟的类库FileSaver.js,其压缩后的大小也就2KB,引入的话也并不会对站点有多大的体积负担。同时我们也需要了解下该pkg的实现,这里梳理下实现逻辑。
saveAs API
先看下API方法的参数情况,可以看到直接传入文件数据或者URL都可。
1 | FileSaver saveAs(Blob/File/Url, optional DOMString filename, optional Object { autoBom }) |
处理流程
- 操作DOM创建a标签元素,由于a标签没有设置content,因此也不会出现在当前页面。
- a元素设置download属性,值即为指定的文件名。
- a元素设置rel值为noopener,设置的好处是性能和安全。
- 判断save存储的是不是字符串
- 是字符串的话,将会被视为URL,然后根据是否跨域决定是不是直接就可利用a标签直接下载保存,如果跨域,download属性不会work,因此必须XHR下载数据然后再作为blob进行下载保存,如果不跨域,a标签直接挂URL和download即可保证下载和保存。
- 非URL的话,blob数据直接作为a元素的href,触发a.click即可。
下载文件而不是打开文件
当我们直接URL下载文件时会发现浏览器会默认打开该文件,如果我们想改变这个行为有几个办法
- 同源文件下载的话,a标签方式下设置download属性即可,浏览器会视为设置了Content-Disposition:attachment,因此会作为附件进行下载。
- 非同源文件下载,download属性不会work,因此需要通过异步下载文件获得blob数据,之后构建a标签保存,或者是服务端返回设置Content-Disposition:attachment。
防止内存泄漏
- 可以留意到,a元素的href在绑定objectURL后,会通过setTimeout然后再执行click下载,之后再延迟进行
URL.revokeObjectURL(a.href)
。这里这么做主要是担心文件过大的情况下,这里会出现内存泄漏,需要手动进行内存回收。 - 这里因为a元素并没有被追加到document.body上,因此并不存在手动删除a元素,所以这里才会走revoke,如果是a元素绑定到document.body上,这里实际上通过删除元素也可以解决。
写在最后
done.