前端下载文件
记录一下在下载文件,文件流时遇到的坑
浏览器环境下载文件到系统有下面两种方法可以实现
window.open()由window对象提供的方法。
window.open('localhost:3000/api/download')
我用的少就不展开讲
<a>标签。
<a herf='localhost:3000/api/download'>下载</a>
mdn上面的描述是 href属性可以通向其他网页,文件,同一文件内的位置,电子邮件地址,或任何url超链接。
所以后端提供的接口是现成的文件或者文件流时,直接将接口地址放到herf属性上,下载文件的接口响应头类型是文件类型时,浏览器会自动的调用系统的下载文件窗口。
所以, 浏览器是怎么识别接口返回内容的类型的?
答案就在这里, 响应头中的 content-type这个属性保存了后端给我们标注的数据的 **MIME ** 类型
拿MS Excel的MIME举例,
application/vnd.ms-excel官方
-
application/msexcel -
application/x-msexcel -
application/x-ms-excel -
application/x-excel -
application/x-dos_ms_excel -
application/xls -
application/x-xls -
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet(xlsx)浏览器识别到这些类型的数据时,会弹出下载的提示框
此时直接点击标签就可以下载了,文件名可以通过a标签的download设置
<a herf='localhost:3000/api/download' download='filename.xlsx'>点击下载</a>
但是 这个方法只适用于GET方法的请求。
因为get请求可以将参数拼接到url上面。
那如果是POST方法,也需要携带参数请求怎么办呢? - ajax
大概步骤如下:
1. 先调用接口,将数据请求回来。
2. 将数据转成`Blob`对象或者`File`对象。
3. 利用web提供的`URL`对象中的`createObjectURL()`方法获取到 Blob/File 对象的本地URl地址。
4. 创建一个a标签并将herf改为刚刚获取到的url地址。
这里解释一下,第一步是请求数据,如果是使用axios请求数据,axios会默认将数据类型转为json, 所以需要手动将responseType设置为blob或者arraybuffer类型。 下图是axios官网示例
第二步是将数据创建一个 Blob或File 对象, Blob对象是一个不可变,存储原始数据的类文件对象,而File对象则继承自Blob对象。 以Blob对象为例, 创建Blob对象需要一个 ArrayBuffer ,ArrayBufferView, Blob, DOMString组成的数组, 和指定的MIME类型。 用大白话来说就是创建一个Blob对象需要 传入一段二进制数据, 和告诉它编译这段二进制数据的方式。
第三步就是利用URL.createObjectURL()获取到Blob的url。
最后创建一个a标签用来下载这个文件。
最后用axios举例代码如下, 也可以用原生ajax,fetch。
const baseUrl = "/api/downloads?";
const data = {
fileId: '001'
}
const res = await axios({
url:baseUrl,
method:"POST",
data,
responseType: "blob" //这一步是关键,因为axios默认会将responseType设置为json,到时候看到的就是乱码
})
if(res.status == 200){
const { headers } = res
let headerStr = headers['content-disposition'] //这一步是为了获取这个字段中保存的filename
let headerList = headerStr.split(';')
let file = headerList.find(item=>/^filename=/.test(item))
let filename
if(file){
filename = file.slice(10, -1)
}
//创建blob对象时,确保传入的二进制数据而不是乱码的中文 res.data
const blob = new Blob([res.data], {type:"application/msexcel;charset=utf-8"})
const url = URL.createObjectURL(blob)
let a = document.createElement("a");
a.style.display = "none";
a.href = url
a.download = filename || '项目.xlsx'
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}