关于文件下载,你需要了解这些

133 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

前言

这里插播一则天气预告,近日受冷空气影响,全国各地出现大幅降温,一夜入冬,在此提醒广大掘友,温度骤降,注意保暖

6226981d8425ab377066eafb237534bf.jpeg

同时

记得给我

1b3cabc3a9a04461c6659193db78eeb5.gif

言归正传

开发中经常会遇到下载文件的功能

一种是拿到后端提供的文件在资源服务器中的地址,使用 a 标签直接打开下载,或使用window.open / window.location.href下载。

另一种方式是后端返回二进制文件流,前端对二进制文件流进行转换得到一个url通过a标签下载

这篇文章分享一下第二种方式的具体做法

一. 代码应用

完整方法:

const onDownload = (id) => {
  API.getReportDownload(id).then((res) => {
    const url = window.URL.createObjectURL(
      new Blob([res.data], { type: 'application/octet-stream;charset=utf-8' })
    )
    const link = document.createElement('a')
    link.style.display = 'none'
    link.href = url
    const fileName =
      res.headers['content-disposition'].split("filename*=utf-8''")[1]
    link.setAttribute('download', decodeURIComponent(fileName))
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  })
}

1. 获取二进制文件流

使用原生ajax或axios 正常调取接口的方法即可

需要注意,请求头中要添加 responseType: 'blob',将返回的二进制文件变为Blob对象,方便接下来使用JS进行操作

axios.get(url, {
      params: params,
      responseType: "blob"  // 添加返回响应的类型
    })
    .then( res => {
         console.log(res)
    })

获取到的二进制流文件如图:

image.png

2. 转换为URL

const url = window.URL.createObjectURL(
  new Blob([res.data], { type: 'application/octet-stream;charset=utf-8' })
)

将 Blob对象转换为 Blob url

3. 动态创建a标签,模拟点击下载

const link = document.createElement('a')
link.style.display = 'none'
link.href = url
 // 这里读取header中提供的文件名,可根据需要决定是否前端写死或后端获取
const fileName =
  res.headers['content-disposition'].split("filename*=utf-8''")[1] 
link.setAttribute('download', decodeURIComponent(fileName))
document.body.appendChild(link)
link.click()
document.body.removeChild(link)

这里需要注意,从header中获取文件部分信息时,如果使用某些封装过的axios请求,有可能将response中的头信息拦截掉,这时应该单独对下载接口做处理

二. 拓展延伸

1. Blob是什么?

在一般的 Web 开发中,很少会用到 Blob,但 Blob 可以满足一些场景下的特殊需求。Blob,Binary Large Object 的缩写,代表二进制类型的对象。Blob 的概念在一些数据库中有使用到,例如,MYSQL中的 BLOB 类型就表示二进制数据的容器。在 Web 中,Blob 类型的对象表示不可变的类似文件对象的原始数据。通俗点说,Blob对象是二进制数据,但它是类似文件对象的二进制数据,因此可以像操作 File 对象一样操作 Blob 对象,实际上,File 继承自Blob。

一直以来,JS都没有比较好的可以直接处理二进制的方法。而Blob的存在,允许我们可以通过JS直接操作二进制数据。

2. Blob API 简介

Blob 由一个可选的字符串 type(通常是 MIME 类型)和 blobParts 组成:MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。·

常见的 MIME 类型有:超文本标记语言文本 .html text/html、PNG图像 .png image/png、普通文本 .txt text/plain 等。

3. Blob构造函数

Blob 构造函数的语法为:

var aBlob = new Blob(blobParts, options);

相关的参数说明如下:·

  • blobParts:它是一个由 ArrayBuffer,ArrayBufferView,Blob,DOMString 等对象构成的数组。DOMStrings 会被编码为 UTF-8。
  • options:一个可选的对象,包含以下两个属性:
    • type —— 默认值为 "",它代表了将会被放入到 blob 中的数组内容的 MIME 类型。
    • endings —— 默认值为 "transparent",用于指定包含行结束符 \n 的字符串如何被写入。它是以下两个值中的一个:"native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持 blob 中保存的结束符不变。

4. Blob 用作 URL

Blob 可以很容易的作为 <a><img> 或其他标签的 URL,多亏了 type 属性,我们也可以上传/下载 Blob 对象。

Blob URL/Object URL 是一种伪协议,允许 Blob 和 File 对象用作图像,下载二进制数据链接等的 URL 源。在浏览器中,我们使用 URL.createObjectURL 方法来创建 Blob URL,该方法接收一个 Blob 对象,并为其创建一个唯一的 URL,其形式为 blob:<origin>/<uuid>,对应的示例如下:

blob:example.org/40a5fb5a-d5…

浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 URL → Blob 映射。因此,此类 URL 较短,但可以访问 Blob。生成的 URL 仅在当前文档打开的状态下才有效。它允许引用 <img><a> 中的 Blob,但如果你访问的 Blob URL 不再存在,则会从浏览器中收到 404 错误。·

上述的 Blob URL 看似很不错,但实际上它也有副作用。虽然存储了 URL → Blob 的映射,但 Blob 本身仍驻留在内存中,浏览器无法释放它。映射在文档卸载时自动清除,因此 Blob 对象随后被释放。

但是,如果应用程序寿命很长,那不会很快发生。因此,如果我们创建一个 Blob URL,即使不再需要该 Blob,它也会存在内存中。

针对这个问题,我们可以调用 URL.revokeObjectURL(url) 方法,从内部映射中删除引用,从而允许删除 Blob(如果没有其他引用),并释放内存。接下来,我们来看一下 Blob 文件下载的具体示例。

总结

  1. 二进制文件流的下载概括为 把后端返回的二进制文件放在 Blob 里面,并且使用Blob 创建一个URL ,再通过JavaScript动态创建一个a标签,使用a标签打开这个 URL 。
  2. 通过拓展了解Blob后,其中的操作我们更容易理解。