最近在搞老项目改造,发现一个导出问题,虽然页面上文件导出成功,但是打开文件后发现是乱码(错误信息),查看network接口状态,发现接口报错了,但是axios拦截器并没有捕获到这个异常,这篇文章记录一下我是怎么处理的,也希望得到大佬们的指导建议和意见。
简化一下必要的基本流程,以保证整体逻辑的连贯性和完整性。
第一步:声明axios实例
const instance = axios.create({
baseURL,
timeout: 30 * 1000,
paramsSerializer: function(params) {
return qs.stringify(params, { arrayFormat: 'repeat' })
}
})
第二步:声明导出接口
{
export() {
return instance({
url: 'api/export',
responseType: 'blob',
method: 'POST'
})
}
}
第三步:封装一个导出函数
function genFile(blob) {
// ...
}
第四步:业务组件调用导出接口(文件导出在axios封装里即可,这里有历史原因,滑过!)
import { export } from '@/api'
export()
.then(res => genFile(res))
.catch(err => {})
问题就出在这里,then回调拿到的res
是错误信息,不符合预期。我们需要在拦截器里处理。
第五步:改造拦截器。提前声明了responseType: 'blob'
,接口返回一定是符合预期的,所以针对二进制流做特殊处理。
instance.interceptors.response.use(async response => {
const { data, config } = response
// 省略其他逻辑
const isBlob = data instanceOf Blob
if (isBlob) {
return capturedBlob(data)
}
})
第六步:声明capturedBlob
函数,专门捕获错误。读取文件流是异步,所以用Promise
做一层包装,还能保持外部继续链式调用。
function capturedBlob(data) {
return new Promise((resolve, reject) => {
const fileReader = new FileReader() // 读取文件流
fileReader.readAsText(data, 'utf-8') // 开始读取 Blob 内容,返回的result属性是一个字符串
fileReader.addEventListener('loadend', () => {
let result = null
try {
result = JSON.parse(fileReader.result) // try包裹,防止异常,比如 csv
} catch {}
if (result && result.code === 10001) {
Message({
type: 'warning',
dangerouslyUseHTMLString: true,
message: result.message || '接口异常!'
})
reject(result)
} else {
resolve(data) // 返回文件流
}
})
})
}
核心在于Blob转JSON,关键点在于,用 FileReader 读取服务器返回的文件流,调用 FileReader.readAsText()
API,通过监听loadend
回调函数,拿到Blob转成JSON后的数据。