axios拦截器,如何捕获Blob二进制文件流错误

312 阅读2分钟

最近在搞老项目改造,发现一个导出问题,虽然页面上文件导出成功,但是打开文件后发现是乱码(错误信息),查看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后的数据。