前言
对于不是从事音视频方面的同学来说,很多状况下都是通过 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-Type
为 application/octet-stream
,它是二进制文件默认的 MIME
类型。
Connection: keep-aliveContent-Disposition: attachment;filename=test.xlsx;filename*=UTF-8Content-Length: 14584Content-Type: application/octet-streamDate: 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/plain
、text/html
、image/jpeg
、application/json
、application/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
上传文件时,对应type
为file
的input
的name
值,例如<input type="file" name="upload" />
,此时对应的name
则为upload
。
须要留神的是,对于Content-Disposition
的formdata
它仅仅是一个信息提醒的作用,并不是实现实体主体内容为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
,通过设置 reposponseType
为 blob
,能够间接将拿到的二进制文件转化为 Blob
。
当然axios
也反对设置reponseType
,并且咱们也能够设置responseType
为arraybuffer
,然而我想没这个必要旁敲侧击。
而后,在拿到二进制文件对应的 Blob
对象后,咱们须要进行下载操作,这里咱们讲一下两种应用 Blob
实现文件下载的形式。
URL.createObjectURL
在浏览器端,咱们要实现下载文件,无非就是借助 a
标签来指向一个文件的下载地址。所以 window.location.href
的实质也是这样。也因而,在咱们拿到了二进制文件对应的 Blob
对象后,咱们须要为这个 Blob
对象创立一个指向它的下载地址 URL
。
而 URL.createObjectURL
办法则能够实现接管 File
或 Blob
对象,创立一个 DOMString,蕴含了对应的 URL
,指向 Blob
或 File
对象,它看起来会是这样:
"blob:http://localhost:8080/a48aa254-866e-4c66-ba79-ae71cf5c1cb3"
残缺的应用 Blob
和 URL.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
一样能够接管 File
或 Blob
对象。
这个过程,次要由两个函数实现 readAsDataURL
和 onload
,前者用于将 Blob
或 File
对象转为对应的 URL
,后者用于接管前者实现后的 URL
,它会在 e.target.result
上。
残缺的应用 Blob
和 FileReader
下载文件的 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 高级类型,你理解几个?
❤️爱心三连击
通过浏览,如果你感觉有播种的话,能够爱心三连击!!!