前言
前几日实现文件导出功能时发现文件下载以后在本地打开报错Excel文件损坏无法打开,但是在Postman直接调用是可以的,查阅多方文档后,找到了解决办法,responseType: "blob"
属性。
具体实现
项目基于axios进行了请求的二次封装具体代码如下:
export function sendGet(
url: string,
params: object = {},
options: object = {}
): Promise<any> {
return new Promise(async (resolve, reject) => {
try {
options["params"] = params;
let res = await http.get(url, options);
if (res) {
resolve(res);
}
} catch (e) {
reject(e);
}
});
}
下载执行的具体接口是:
// 接口封装
import {
`sendGet`
} from "方法封装地址";
//
export const loadTemplate = data =>
sendGet(`具体业务接口地址`, data, {
responseType: "blob"
});
后端返回的是二进制流数据,如果在接口api定义时不设置responseType: "blob"
,下载后的文件就是损坏的。
在页面通过事件触发:
loadTemplate().then(res => {
console.log(res);
createExcelFile(
res,
"模板定义名称",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
);
});
在函数loadTemplate
执行成功拿到的res其实就是后端返回的二进制文件流,然后通过createExcelFile
下载函数进行文件下载。
文件下载方法为了方便扩展,新增了一个文件类型的字段,可以当参数的形式进行传递,方便多种文件的下载支持。
/**
* 文件下载
* @param {} res - 二进制文件流
* @param {string} fileName - 下载后文件名定义
* @param {string} type - 下载的文件类型
*/
export default (res, fileName, type = "application/vnd.ms-excel") => {
const blob = new Blob([res], { type: type }); //type这里表示默认的下载文件类型为xlsx类型
const downloadElement = document.createElement("a");
const href = window.URL.createObjectURL(blob); //创建下载的链接
downloadElement.href = href;
downloadElement.download = fileName; //下载后文件名
document.body.appendChild(downloadElement);
downloadElement.click(); //点击下载
document.body.removeChild(downloadElement); //下载完成移除元素
window.URL.revokeObjectURL(href);
};
但是这样就会出现一个问题,如果后端同学没有给你返回二进制文件流就会报错,当然如果约定好了也不会这么做,但是为了避免这样的情况发生,我也做了相应的处理,新增一个判断方法。
// 记住,这个地方传入的是response,而不是请求拿到的response.data
function judgeErrorByResponseType(response) {
return new Promise((resolve, reject) => {
if (response.headers['content-type'].includes('json')) {
// 此处拿到的data才是blob
const { data } = response
const reader = new FileReader()
reader.onload = () => {
const { result } = reader
const errorInfos = JSON.parse(result)
const { msg } = errorInfos
reject(new Error(msg))
}
reader.onerror = err => {
reject(err)
}
reader.readAsText(data)
} else {
resolve(response)
}
})
}
这个方法的编写的时候对后台也是有要求的,就是要他准确的返回 content-type。如果是 JSON 数据格式的错误信息,就必须得返回application/json。
上面的judgeErrorByResponseType很简单,需要在下载前做个判断就行。
loadTemplate().then(res => {
console.log(res);
const response = judgeErrorByResponseType(res)
createExcelFile(
response,
"模板定义名称",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
);
});
后续
在查找问题的时候看到很多文章写道mockjs的引入会影响responseType,但是我这里没有用到,作为避坑指南也分享给大家吧mockjs影响axios下载responseType属性
希望本文可以帮助更多遇到相同的问题的前端同学,让大家少走弯路~~~