axios文件下载爬坑记录

2,383 阅读2分钟

前言

前几日实现文件导出功能时发现文件下载以后在本地打开报错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属性

希望本文可以帮助更多遇到相同的问题的前端同学,让大家少走弯路~~~