filesaver源码解读

网页实现文件下载可以使用成熟的类库FileSaver.js,其压缩后的大小也就2KB,引入的话也并不会对站点有多大的体积负担。同时我们也需要了解下该pkg的实现,这里梳理下实现逻辑。

saveAs API

先看下API方法的参数情况,可以看到直接传入文件数据或者URL都可。

1
FileSaver saveAs(Blob/File/Url, optional DOMString filename, optional Object { autoBom })

处理流程

  1. 操作DOM创建a标签元素,由于a标签没有设置content,因此也不会出现在当前页面。
  2. a元素设置download属性,值即为指定的文件名。
  3. a元素设置rel值为noopener,设置的好处是性能和安全。
  4. 判断save存储的是不是字符串
    1. 是字符串的话,将会被视为URL,然后根据是否跨域决定是不是直接就可利用a标签直接下载保存,如果跨域,download属性不会work,因此必须XHR下载数据然后再作为blob进行下载保存,如果不跨域,a标签直接挂URL和download即可保证下载和保存。
    2. 非URL的话,blob数据直接作为a元素的href,触发a.click即可。

下载文件而不是打开文件

当我们直接URL下载文件时会发现浏览器会默认打开该文件,如果我们想改变这个行为有几个办法

  1. 同源文件下载的话,a标签方式下设置download属性即可,浏览器会视为设置了Content-Disposition:attachment,因此会作为附件进行下载。
  2. 非同源文件下载,download属性不会work,因此需要通过异步下载文件获得blob数据,之后构建a标签保存,或者是服务端返回设置Content-Disposition:attachment。

防止内存泄漏

  1. 可以留意到,a元素的href在绑定objectURL后,会通过setTimeout然后再执行click下载,之后再延迟进行 URL.revokeObjectURL(a.href)。这里这么做主要是担心文件过大的情况下,这里会出现内存泄漏,需要手动进行内存回收。
  2. 这里因为a元素并没有被追加到document.body上,因此并不存在手动删除a元素,所以这里才会走revoke,如果是a元素绑定到document.body上,这里实际上通过删除元素也可以解决。

写在最后

done.