主题
axios获取responseType为blob时请求的错误信息
前言
在前后端交互中,文件下载(导出)是很常规的操作,假设前端用axios
进行文件下载,需要在请求中加入responseType:blob
来设置以流的形式进行响应,这样之后,请求成功时返回的是一个流形式的文件,正常导出文件。但是请求失败的时候后端返回的是json ,不会处理错误信息,而是直接导出包含错误信息的文件。用户体验不是很好,我想要的效果是,请求成功时候正常下载文件,请求失败时不下载文件,仅仅将错误信息展示出来。
1. 思路
通常在封装的axios
中我们都是以后端返回的code
值进行判断,因此就没有办法获取到后端返回的错误信息进行提示
解决方案邮很多,这里说两种:
- 第一种, 默认
responseType
为json
,然后请求成功之后将json
格式转化成blob
在进行导出 - 第二种,设置
responseType = "blob"
,请求失败之后将blob
转化成json
格式
这里用第二种方式进行举例
2. axios
设置responseType:'blob'
2.1 对某个请求设置
js
axios({
method: "请求方式",
url: "接口路径",
data: "接口参数",
dataType: 'json',
responseType: 'blob',
headers: {},
data:{}, // body参数
params:{} // queryString参数
})
.then(response => {})
.catch(error => {})
2.2 统一设置
js
const service = axios.create({
baseURL:"xxxxx",
responseType:"blob"
})
// 或者
service.defaults.responseType = 'blob'
3 获取错误Msg
3.1 针对某个请求获取
js
// 省略接口请求
// .......
getData(params).then(response => {
const resData = response.data
const fileReader = new FileReader()
fileReader.onloadend = () => {
// 此处两种判断返回信息的方法可选其一
// 方法一:
try {
const jsonData = JSON.parse(fileReader.result) // 说明是普通对象数据,后台转换失败
// 后台信息
console.log(jsonData)
} catch (err) { // 解析成对象失败,说明是正常的文件流
// 下载文件
downloadFile(resData)
}
// 方法二:
if (resData.type === 'application/json') {
const jsonData = JSON.parse(fileReader.result) // 说明是普通对象数据,后台转换失败
// 后台信息
console.log(jsonData)
} else {
// 下载文件
downloadFile(resData)
}
}
fileReader.readAsText(resData)
})
function downloadFile (resData) {
const link = document.createElement('a')
let blob = new Blob([resData], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8'})
link.style.display = 'none'
link.href = URL.createObjectURL(blob)
link.setAttribute('download', '下载文件.xlsx') // 文件名可自定义
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
jsonData
就是后台的报错信息。如果使用第二种方法,建议改为下面的代码,即下载文件时不需要FileReader
:
js
// 省略接口请求
// .....
getData(params).then(response => {
const resData = response.data
if (resData.type === 'application/json') {
// 说明是普通对象数据,读取信息
const fileReader = new FileReader()
fileReader.onloadend = () => {
const jsonData = JSON.parse(fileReader.result)
// 后台信息
console.log(jsonData)
}
fileReader.readAsText(resData)
} else {
// 下载文件
downloadFile(resData)
}
})
3.2 拦截器统一获取
js
// 响应拦截器
service.interceptors.response.use(
response => {
if (response.headers['content-type'] === 'application/json') {
const res = response.data
// 针对文件下载失败时候的处理
if (res.size) {
// 将blob转为json
const reader = new FileReader()
let parseObj = null
reader.readAsText(response.data, 'utf-8')
reader.onload = function() {
parseObj = JSON.parse(reader.result)
Message.error(parseObj.message || 'Error')
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
if (res.code !== '200') {
// Message({
// message: res.message || 'Error',
// type: 'error',
// duration: 5 * 1000
// })
Message.error(res.message || 'Error')
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
}
} else {
return response
}
})
4. 文件下载公共方法
注意
一些内置的变量以及方法,不要可以删除,Vue2里面, 可以用mixin来处理更为方便, vue3用hook更为方便
js
/**
* 通过APi接口下载文件的通用方法
* @Description 传入接口以及入参, 自动下载文件<br>
* 1. 内置 <b>this.downloadLoading</b> 状态,可用于控制下载按钮是否处于loading状态<br>
* 2. 内置操作日志上报接口<br>
* 3. 可将下载接口返回JSON时候的msg提示出来
* @param {*} method 文件下载请求方法接口
* @param {*} data 接口入参
* @param {*} addLogParams 操作日志入参(内部有默认值)
* @returns
*/
downLoadFileByApi(method, data = {}, addLogParams = {}) {
if (Object.prototype.toString.call(method) !== "[object Function]") {
throw "method参数必须是一个Function";
}
addLogParams = {
subFunction: "导出",
type: "exportFile",
result: data,
isSuccess: "1",
...addLogParams
};
// this.downloadLoading = true; // 禁用下载按钮
method(data)
.then(response => {
// response是axios返回的response, 没经过处理
const resData = response.data;
// 说明是普通对象数据,读取信息
if (resData.type === "application/json") {
const fileReader = new FileReader();
fileReader.readAsText(resData);
fileReader.onloadend = () => {
const jsonData = JSON.parse(fileReader.result);
// 后台信息
const { code, msg } = jsonData;
if (code === 0) {
this.$message.waning(msg);
} else {
this.$message.error(msg);
}
// this.downloadLoading = false; // 解除禁用下载按钮
// this.handleLogFunction(addLogParams);
};
} else {
let filename = decodeURIComponent(
response.headers["content-disposition"]
);
filename = filename.split("=")[1].replace("UTF-8''", "").trim();
const blob = resData;
let link = document.createElement("a");
link.style.display = "none";
link.href = URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(link.href); //释放掉blob对象
document.body.removeChild(link); //下载完成移除元素
// this.$message({ message: '操作成功', type: 'success' });
// this.downloadLoading = false; // 解除禁用下载按钮
// addLogParams.isSuccess = '0'
// this.handleLogFunction(addLogParams);
}
})
.catch(error => {
// this.$message.error('error.message)
// this.downloadLoading = false; // 解除禁用下载按钮
// addLogParams.isSuccess = error.isSuccess ? error.isSuccess : '1';
// this.handleLogFunction(addLogParams);
});
}
文件下载参考
js
// get 请求
axios({
method: 'get',
url, // 这里自行设置传参
params, // 这里自行设置传参
responseType:'blob' // 这里是重点,敲黑板
}).then(res => {
// 获取响应头中的文件名
let filename = response.headers //下载后文件名
filename = filename["content-disposition"]
filename = filename.split(";")[1].split("filename=")[1]
// 创建 a 标签
let elink = document.createElement('a');
document.body.appendChild(elink);
elink.style.display = 'none'; // 隐藏起来
elink.download = filename
// 如果后端没有返回,可以自己设置下载文件的名称 elink.download = 'XXX文件.pdf';
let blob = new Blob([res.data]);
// 兼容webkix浏览器,处理webkit浏览器中herf自动添加blob前缀,默认在浏览器打开而不是下载
const URL = window.URL || window.webkitURL
elink.href = URL.createObjectURL(blob) // 通过createObjectURL方法转换成对象url
elink.click();
document.body.removeChild(elink);
URL.revokeObjectURL(elink.href); // 释放URL 对象
})