项目中请求后端接口下载一个docx文件。
Response如下:
这时首先想到的是后端返回了二进制流,需要将流转换为Blob,生成downloadUrl,使用a标签下载。
Ctrl V 百度的代码:
axios({
url: 'http://localhost:5000/download',
method: 'POST',
data: {
//...
},
responseType: 'blob', // 设置服务器响应的数据类型(必选)
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.download = 'file.docx';
link.click();
});
然后愉快的测试,word下载成功。
继续打开文件,报错:很抱歉,无法打开xxx.docx,因为内容有问题。
内容有毛问题?点击确定:Word 在 xxx.docx 中发现无法读取的内容,是否恢复此文档的内容?如果您信任此文档的来源,请点击“是”
点击“是”还是无法打开,一定是后端的问题,过去撕逼。
然后被打脸:后端使用API文档工具测试,下载的文件可以正常打开。
继续回到座位百度,答案基本一样,观察细节尝试各种办法(可以直接跳转到最后查看成功的办法):
在创建Blob时指定type
:
let blob = new Blob([response.data], {type: 'application/msword'}) // 不行
let blob = new Blob([response.data], {type: 'application/octet-stream'}) // 不行
把正常的和不正常的docx文件后缀改为txt对比,发现虽然开头都是PK
,但是我下载的文件中有大量的�
。
而后端下载的文件内容虽然也看不懂(鱀咯暜==|eg?~€澐Y}跿憓
之类的),但是没有�
。
难道是编码问题?
再看看F12的response,返回的本来就是带�
的,忍住找后端的冲动。
开始从编码着手,可是项目默认请求的Content-Type就是 UTF-8 啊。
先试着请求时手动设置 Content-Type ,并没有解决。
axios({
url: 'http://localhost:5000/download',
method: 'POST',
data: {
//...
},
headers: {
'content-type': 'application/json;charset=UTF-8'
},
responseType: 'blob', // 设置服务器响应的数据类型(必选)
}).then((response) => {
//...
});
设置 responseType: 'arraybuffer'
,并没有解决
然后又百度到用 Base64 转码,尝试了,并没有解决。
再次console打印接口返回的内容:
responseType: 'blob'
设置成功了呀。
终于百度到一个靠谱的大神,多亏他回答问题是作了额外的说明(感激不尽)。
大神原话:
可以明显的看到,返回的response在的data 是一个blob Object。
createObjectURL 函数,接受的参数是blob 类型或者是File 类型。
所以说,我想创建的URL 对象只需要 如下代码
let url = URL.createObjectURL(response.data)
因为response.data 已经是一个blob Object 了,完全不需要再像let url = window.URL.createObjectURL(new Blob([response.data]))
载入
百度到的回答,都是用new Blob
创建一个Blob,所以我完全没质疑过这个使用,并且没有查看过createObjectURL
的使用方法。
于是调整代码,再次测试,word可以正常打开了。
axios({
url: 'http://localhost:5000/download',
method: 'POST',
data: {
//...
},
responseType: 'blob', // 设置服务器响应的数据类型(必选)
}).then((response) => {
const url = window.URL.createObjectURL(new Blob(response.data)); // 调整了这里
const link = document.createElement('a');
link.href = url;
link.download = 'file.docx';
link.click();
});
有时由于封装 Axios 造成传参不规范,例如本项目中封装的请求,就把 responseType 包裹在 headers 中,导致开发人员工具中Request Header 中虽然显式了 responseType: blob
但是并没有没生效。
排查这个问题浪费了点时间,最终还是查看打印的 Axios 返回结果的 request.responseType为空才发现。
网上也有说使用mock的某些场景会把 responseType 默认修改为''
papersnake 的回答