前端实现文件下载的几种方式

644 阅读3分钟

后端控制下载

后端在 Http 请求响应的 Header 中添加 Content-Disposition 头, 那么前端只需要创建一个 <a> 标签即,href指向下载地址,可实现下载,且 <a> 不需要 download 属性。该方法不论在PC还是H5(包括Safari),都会直接触发下载,不会触发预览操作。

# 属性值第一个必须为 attachment,第二个为下载的文件名
Content-Disposition: attachment; filename="xxx.jpeg"

前端下载

如果链接地址没有返回 Content-Disposition 字段,只能靠在前端启动下载了。

<a> 标签

基于<a>标签的下载文件方式,使用方法如下:

<!-- href: 文件的绝对/相对地址 -->
<!-- download: 文件名(可省略,省略后浏览器自动识别源文件名)--> 
<a href='xxx.jpg' download='file.jpg'>下载jpg图片</a>

缺点是对于对于浏览器可预览文件,浏览器会进行预览,不会下载。

什么是可预览文件:指浏览器可直接预览的文件类型,如 txt、png、jpg、gif、pdf 等。

如果可预览文件(如png)的 content-type 是这么设置:Content-Type: application/octet-stream,其实也是变相的告诉浏览器该文件是二进制文件,从而欺骗浏览器触发下载,当然这也不是前端能控制的。

window.open 或 location.href

<a> 标签的行为一致,可预览即预览,不可预览即下载。

iframe

const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = "xxx";
document.body.appendChild('iframe')

该方式也能实现下载,但同样,对于可预览文件iframe里就直接预览了文件(虽然看不到iframe)。

缺点:

  • 和上面的一样,同样有分可预览不可预览的问题。
  • 每下载一次 append 一个 iframe,试了网上很多代码通过 onload 事件来 remove iframe,亲测行不通。
  • 有兼容性问题,另外发现在 iOS safari 浏览器中也是无法下载,不建议使用。

blob

即手动下载,主要步骤就是:

  1. 通过 fetch 或 其他方式,将数据下载下来后转换成 blob 形式
  2. 调用 URL.createObjectURL 生成 blob url
  3. 动态创建一个 a 标签
  4. 将 blob url 赋给 a,并使用代码触发其 click 事件

这种方式使用的较为广泛,因为不受浏览器可预览文件的影响。

缺点就是如果文件很大,文件必须全部下载完成后才会出发下载,会有很长的等待期。

canvas内容下载

有一种场景,比如我们下载图片前需要在图片上加上水印,即前端加水印。这个时候,我们就需要将图片先绘制到 canvas 上,然后绘制水印,最后通过 canvas.toBlob() 将数据转换成 blob 格式,然后使用上述 blob 方式下载即可。

有一点需要注意,如果 canvas 上绘制的 Image 不支持跨域(即使图像数据请求成功了),那么调用 toBlob() 方法将报错。需要后端进行跨域设置。

file-saver

功能强大的前端实现文件下载的库,如果能满足要求,建议直接使用,地址:file-saver

从其声明可以看出,支持对 urlBlobFile 三种内容的下载,完全由内部处理进行下载,且具有较好的兼容性。

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