记录一次填坑过程(vue项目使用封装的axios请求后端get接口下载文件)
开发过程中,时常遇到有需要导出excel文件的需求。后端的接口调用后通常返回的结果如下
于是开始各种百度如何将后端返回的内容正确解析并下载。
网上有好多方案:
最常见的莫过于:
方案一:
大致的意思就是将流文件利用Blob构造函数,构造一个Blob对象,再利用URL的createObjectURL生成一个链接,最后将这个链接赋值给一个动态生成的a标签的href属性,最后触发一下click事件。文件就下载成功。
然后,直接打开现在好的文件,突然发现成这样了:
开始怀疑是不是后端同学的代码有些问题,询问后端同学,他们没有做任何特别处理,就是直接将数据库里的文件读出来之后返回给了前端。百思不解,百度一圈发现并没有自己想要的答案。
方案二:
记得之前用个一个FileSaver.js的插件,npm安装一下之后,直接按文档引入即可,使用方式如下:
import { saveAs } from 'file-saver';
...此处省略代码....
saveAs(new Blob([res], {type: 'application/vnd.ms-excel'}), 'xxx.xls')
res--后端接口返回的内容,就是图一那一堆乱码, type: 文件类型,对应响应头内的Content-Type字段值即可(具体文件类型对应表); xxx.xls 文件名 一顿操作试完之后,还是不行。我蒙了,后端人员也不会,百度也没有。
突然想起来之前的项目里也有类似的下载,不如跑一下另外一个项目,对比一下请求头、响应头,看看两次请求有没有什么区别:
仔细一对比,好像有一个区别,那就是第一张图的请求头内有多一个Content-type:application/vnd.ms-excel 询问后端人员,他们说get请求这个无所谓,他们不需要,我尝试着在公共封装的axios中的headers添加此字段,发现添加不上,就是代码里虽然有,但是请求头中缺始终没有。于是,我又百度。 结果如这篇文章描述axios get请求如何设置Content-Type: 结论就是axios的源码做了处理。 我们可以通过这种写法绕过axios的源码判断逻辑
if(config.method === "get"){
config.data = {}
config.headers['Content-type'] = 'application/json'
}
感觉大功告成,一运行,下载的文件还是不能正常打开。再仔细跟正常的一对比发现,Content-type字段区分大小写,靠,虽然我们上面的代码写的是Content-type,但是请求的headers中确实Content-Type
于是我用最原生的代码写了ajax请求
let urlPath = xxx; //带参数的完成请求地址
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
let res = 'response' in xhr ? xhr.response : xhr.responseText;
let type = "application/vnd.ms-excel";
// 此处的saveAs是引用的插件fileSaver.js
saveAs(new Blob([ res ], {type: type}), '123.xls')
}
}
let urlPath = this.getApiUrl(apiName)
xhr.open('get', urlPath);
xhr.setRequestHeader("token", localStorage.getItem('token'));
xhr.setRequestHeader("Content-type", "application/json");
xhr.responseType = "arraybuffer";
xhr.send(null)
注意:此处的saveAs代码也可以换做使用a标签那种下载写法
到此,文件终于可以正常下载