后端返回文件流,前端生成链接下载文件

931 阅读2分钟

项目的需求, 点击导出下载一个excel文件,之前以为跟以前一样点击一个链接就给我下载一个文件.这次结果不一样了,后端给我一个接口,我请求接口之后返回的是这样的东西

image.png 跟之前的数据接口完全不一样了.第一次遇到

查看了网上各种情况:

第一步: responseType: 'arrayBuffer' 这个一定要

第二步:

      let filename = '';
      const disposition = response.headers.get('Content-Disposition');
      if (disposition && disposition.includes('attachment')) {
        const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        const matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) {
          filename = matches[1].replace(/['"]/g, '');
        }
      }

这有两个问题:

  1. 最开始response.headers.get('Content-Disposition') 是没有值的, 这是因为后端没有把这个Content-Disposition暴露出来允许前端获取. 参考文章 blog.csdn.net/PGguoqi/art… 按照文章后端处理一下, 这里就可以获取到Content-Disposition从而就可以解析出filename了

  2. 因为这个是文件名,很有可能是含有中文的, 所有有可能是看起来是乱码,像这样:

image.png 有详细的原因: 参考my.oschina.net/pingpangkua… 我们是后端用urlencode处理一下,前端用decodeURI(fileName)解析这样就对了

第三步:

   const link = document.createElement('a');
      response?.blob().then((res: any) => {
        const blob = new Blob([res], { type: 'application/x-xls' });
        const fileName = decodeURI(filename);
        link.style.display = 'none';
        link.href = URL.createObjectURL(blob);
        link.setAttribute('download', fileName);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      });

用Blob生成一个链接再赋值给a标签的href 模拟一个点击事件,就在浏览器现成下载了 我这里用的response?.blob().then 我看网上其他人都是直接res.data 难道是跟我们responseType不同有关系吗? 他们建议是responseType='blob' 我试过,但是还是一样的

我这里的blob()是在reponse的__proto__里面

最后就完成的这个看起来简单的需求.

完整代码:

/** 请求返回统一拦截 */
request.interceptors.response.use(
  async (response: Response, options: RequestOptionsInit) => {
    // 以二进制文件流的方式下载文件
    if (options.responseType === 'arrayBuffer') {
      let filename = '';
      const disposition = response.headers.get('Content-Disposition');
      if (disposition && disposition.includes('attachment')) {
        const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        const matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) {
          filename = matches[1].replace(/['"]/g, '');
        }
      }

      const link = document.createElement('a');
      response?.blob().then((res: any) => {
        const blob = new Blob([res], { type: 'application/x-xls' });
        const fileName = decodeURI(filename);
        link.style.display = 'none';
        link.href = URL.createObjectURL(blob);
        link.setAttribute('download', fileName);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      });
      return { errCode: 0, data: [] };
    }

    const res = await response.clone().json();
    const { errCode, data } = res;

    if (errCode === '401' || errCode === '1110001') {
      // token 失效重新登录
      // handleTokenInvalid();
      // 清空当前所有的弹窗
      HxDrawer.destory();
      history.push('/login');
      return {
        code: '0',
        data,
        errCode,
        msg: '登录已失效,请重新登录!',
      };
    }

    return {
      ...res,
      data: handleNullData(data),
    };
  },
);