JS解析后端流文件后无法正常打开

393 阅读3分钟

记录一次填坑过程(vue项目使用封装的axios请求后端get接口下载文件)

开发过程中,时常遇到有需要导出excel文件的需求。后端的接口调用后通常返回的结果如下

屏幕快照 2021-12-06 下午5.14.45.png 于是开始各种百度如何将后端返回的内容正确解析并下载。 网上有好多方案: 最常见的莫过于:

方案一:

屏幕快照 2021-12-06 下午5.18.12.png

大致的意思就是将流文件利用Blob构造函数,构造一个Blob对象,再利用URL的createObjectURL生成一个链接,最后将这个链接赋值给一个动态生成的a标签的href属性,最后触发一下click事件。文件就下载成功。

然后,直接打开现在好的文件,突然发现成这样了:

屏幕快照 2021-12-06 下午5.24.47.png

开始怀疑是不是后端同学的代码有些问题,询问后端同学,他们没有做任何特别处理,就是直接将数据库里的文件读出来之后返回给了前端。百思不解,百度一圈发现并没有自己想要的答案。

方案二:

记得之前用个一个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 文件名 一顿操作试完之后,还是不行。我蒙了,后端人员也不会,百度也没有。

突然想起来之前的项目里也有类似的下载,不如跑一下另外一个项目,对比一下请求头、响应头,看看两次请求有没有什么区别:

WechatIMG7.png

WechatIMG12.png

WechatIMG9.png

仔细一对比,好像有一个区别,那就是第一张图的请求头内有多一个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标签那种下载写法


到此,文件终于可以正常下载

WechatIMG14.png