关于前端:详解从后端导出文件到前端Blob下载过程

16次阅读

共计 4366 个字符,预计需要花费 11 分钟才能阅读完成。

前言

对于不是从事音视频方面的同学来说,很多状况下都是通过 window.location.href 来下载文件。这种形式,个别是前后端的登录态是基于 Cookie + Session 的形式,因为浏览器默认会将本地的 cookie 塞到 HTTP 申请首部字段的 Set-Cookie 中,从而实现来带用户的 SessionId,所以,咱们也就能够用 window.location.href 来关上一个链接下载文件。

当然,还有一种状况,不须要登录态的校验(比拟 che)。

家喻户晓,还有另一种登录态的解决形式 JWT (JSON Web Token)。这种状况,个别会要求,前端在下载文件的时候在 申请首部 字段中增加 Token 首部字段。那么,这样一来,咱们就不能间接通过 window.location.href 来下载文件。

不过,侥幸的是咱们有 Blob,它是浏览器端的 类文件对象 ,基于二进制数据,咱们能够通过它来 优雅地 解决文件下载,不限于音视频、PDF、Excel 等等。所以,明天咱们就从后端导出文件到 HTTP 协定、非简略申请下的 预检申请 、以及最初的 Blob 解决文件,理解一番 何为其然、如何使其然?

后端(Koa2)导出文件(Excel)

首先,咱们从后端导出文件讲起。这里,我抉择 Koa2 来实现 Excel 的导出,而后搭配 node-xlsx 这个库,从而实现 Excel 的二进制数据的导出。它看起来会是这样:

const xlsx = require("node-xlsx")

router.get("/excelExport", async function (ctx, next) {
    // 数据查问的后果
    const res = ["五柳", "22"]
    // 生成 excel 对应的 buffer 二进制文件
    const excelFile = xlsx.build([{name: "firstSheet", data: res}])
    // 设置文件名相干的响应首部字段
    ctx.set("Content-Disposition", "attachment;filename=test.xlsx;filename*=UTF-8")
    // 返回给前端
    ctx.body = buffer;
  });

这里就不对数据库查问做开展,只是模仿了一下查问后的后果 res

而后咱们用浏览器申请一下这个接口,咱们会看到在响应首部(Response Headers)字段中的 Content-Typeapplication/octet-stream,它是二进制文件默认的 MIME 类型。

Connection: keep-alive
Content-Disposition: attachment;filename=test.xlsx;filename*=UTF-8
Content-Length: 14584
Content-Type: application/octet-stream
Date: Sun, 23 Aug 2020 11:33:16 GMT

MIME 类型,即 Multipurpose Internal Mail Extension,用于示意文件、文档或字节流。

HTTP 协定意识二进制文件流

如果,咱们没有参加后端返回 Excel 的这个过程。那么,HTTP 协定能够帮忙咱们缩小交换,并且懂得咱们前端须要如何进行相应的解决。这里会波及到三个 HTTP 实体首部字段:

  • Content-Type
  • Content-Length
  • Content-Disposition

那么,咱们别离来看看它们在 HTTP 文件传输过程中的 非凡意义

Content-Type

Content-Type 我想这个陈词滥调的 实体首部字段,大家不会生疏。它用来阐明实体主体内容对象的媒体类型,遵循 type/subtype 的构造。常见的有 text/plaintext/htmlimage/jpegapplication/jsonapplication/javascript 等等。

在咱们这里 二进制文件,它没有特定的 subtype,即都是以 application/octet-stream 作为 Content-Type 的值。即如下面咱们所看到:

Content-Type: application/octet-stream

所以,只有咱们相熟 Content-Type,那么在开发中的交换老本就能够缩小。

Content-Length

Content-Length 又是一个眼生的 实体首部字段,它示意传输的实体主体的大小,单位为字节。而在咱们这个栗子,示意传输的 Excel 二进制文件大小为 14584

Content-Disposition

Content-Disposition 这个 实体首部字段 ,我想前端同学大多数是会有陌生感。它用来示意实体主体内容是用于 显示在浏览器内 作为文件下载。它对应的 value 有这么几个内容:

  • formdata,示意实体主体是 formdata 的模式。
  • inline,示意实体主体内容显示在浏览器内。
  • attachment,示意实体主体内容作为文件下载。
  • filename,示意文件编码格局或文件名,例如 filename*=UTF-8 示意文件的编码,filename=test.xlsx 示意下载时的文件名。
  • name,示意 formdata 上传文件时,对应 typefileinputname 值,例如 <input type="file" name="upload" />,此时对应的 name 则为 upload

须要留神的是,对于 Content-Dispositionformdata 它仅仅是一个信息提醒的作用,并不是实现实体主体内容为 formdata,这是 Content-Type 负责的。

那么回到,明天这个栗子,它的 Content-Disposition 为:

Content-Disposition: attachment;filename=test.xlsx;filename*=UTF-8

所以,当初咱们晓得它次要做了这么几件事:

  • 告知浏览器须要将二进制文件 作为附件下载
  • 附件的文件名为 test.xlsx
  • 附件对应的编码为 UTF-8

Blob 优雅地解决文件(Excel)下载

为什么说是 优雅 ?因为,Blob 它能够解决很多类型文件,并且是 受控的 ,你能够管制从接管到二进制文件流、到转化为 Blob、再到用其余 API 来实现下载文件。因为,如果是 window.location.href 下载文件,诚然也能够达到一样的成果,然而你无奈在拿到二进制文件流到下载文件之间做 个性化 的操作。

并且,在简单状况下的文件解决,Blob 必然是首要抉择,例如 分片上传 下载 音视频文件 的拼接等等。所以,在这里我也推崇大家应用 Blob 解决文件下载。

并且,值得一提的是 XMLHttpRequest 默认反对了设置 responseType,通过设置 reposponseTypeblob,能够间接将拿到的二进制文件转化为 Blob

当然 axios 也反对设置 reponseType,并且咱们也能够设置 responseTypearraybuffer,然而我想没这个必要旁敲侧击。

而后,在拿到二进制文件对应的 Blob 对象后,咱们须要进行下载操作,这里咱们讲一下两种应用 Blob 实现文件下载的形式。

URL.createObjectURL

在浏览器端,咱们要实现下载文件,无非就是借助 a 标签来指向一个文件的下载地址。所以 window.location.href 的实质也是这样。也因而,在咱们拿到了二进制文件对应的 Blob 对象后,咱们须要为这个 Blob 对象创立一个指向它的下载地址 URL

URL.createObjectURL 办法则能够实现接管 FileBlob 对象,创立一个 DOMString,蕴含了对应的 URL,指向 BlobFile 对象,它看起来会是这样:

"blob:http://localhost:8080/a48aa254-866e-4c66-ba79-ae71cf5c1cb3"

残缺的应用 BlobURL.createObjectURL 下载文件的 util 函数:

export const downloadFile = (fileStream, name, extension, type = "") => {const blob = new Blob([fileStream] type);
  const fileName = `${name}.${extension}`;
  if ("download" in document.createElement("a")) {const elink = document.createElement("a");
    elink.download = fileName;
    elink.style.display = "none";
    elink.href = URL.createObjectURL(blob);
    document.body.appendChild(elink);
    elink.click();
    URL.revokeObjectURL(elink.href);
    document.body.removeChild(elink);
  } else {navigator.msSaveBlob(blob, fileName);
  }
};

FileReader

同样地,FileReader 对象也能够使得咱们对 Blob 对象生成一个下载地址 URL,它和 URL.createObject 一样能够接管 FileBlob 对象。

这个过程,次要由两个函数实现 readAsDataURLonload,前者用于将 BlobFile 对象转为对应的 URL,后者用于接管前者实现后的 URL,它会在 e.target.result 上。

残缺的应用 BlobFileReader 下载文件的 util 函数:

const readBlob2Url  =  (blob, type) =>{
  return new Promise(resolve => {const reader = new FileReader()
    reader.onload = function (e) {resolve(e.target.result)
    }
    reader.readAsDataURL(blob)
  })
}

写在最初

如果,仅仅是用一个 Blob 这个浏览器 API 解决文件下载,可能带给你的收益并没有多少。然而,通过理解从后端文件导出、HTTP 协定、Blob 解决文件下载这整个过程,就能够构建一个 残缺的技术思维体系 ,从而获取其中的收益。 唯有知其然,方能使其然。 这也是前段时间看到的很合乎咱们作为一个一直学习的从业者的态度。也因而,良好的技术常识储备,能让咱们领有很好的编程思维和设计思维。

往期文章回顾

  • 从零到一,带你彻底搞懂 vite 中的 HMR 原理(源码剖析)
  • TypeScript 高级类型,你理解几个?

❤️爱心三连击

通过浏览,如果你感觉有播种的话,能够 爱心三连击!!!

正文完
 0