问题场景

点击导出按钮,提交请求,下载excel大文件(超过500M),该文件没有预生成在后端,
直接以文件流的形式返回给前端。

解决方案

在Vue项目中常用的方式是通过axios配置请求,读取后端返回的文件流,常用代码如下:

axios({
    method: 'post',
    url: 'api/file',
    responseType: 'blob'
}).then(res=> {
     if (res.data){
      filename = 'filename';
      let blob = new Blob([res.data],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"});
      if (window.navigator.msSaveOrOpenBlob){
          // IE10+下载
        navigator.msSaveOrBlob(blob, filename);
      }else{
          // 非IE10+下载
        let link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = filename;
        document.body.appendChild(link);
        var evt = document.createEvent("MouseEvents");
        evt.initEvent("click", false, false);
        link.dispatchEvent(evt);//释放URL 对象
        document.body.removeChild(link);
      }
}).catch((error) => {
  console.log(error)
})

这种方式是把文件流读取到浏览器内存中,再下载,但是今天在这种大文件场景下它不香了,
由于内存过大,直接把网页给搞崩了,喔豁😂

前端JS 下载大文件解决方案-LMLPHP

怎么办呢,终于在Github上找到了一个大神的库,用起来真香,Github地址

根据介绍,在chrome浏览器2G以下的文件下载可以得到很好的支持

使用步骤

1.安装npm依赖

npm install file-saver --save

2.引入代码

+ import { saveAs } from 'file-saver';
...
+      saveAs(blob, fileName );

3.完整例子

+ import { saveAs } from 'file-saver';
  axios({
    method: 'post',
    url: 'api/file',
    responseType: 'blob'
}).then(res=> {
     if (res.data){
       fileName = this.fileName;
      // 有文件名就用自定义的,没有就从header获取
        if (!fileName) {
          fileName = fileNameFromHeader(
            res.headers["content-disposition"] || ""
          );
        }

      let blob = new Blob([res.data],{
      type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"});
+      saveAs(blob, fileName );
      }
}).catch((error) => {
  console.log(error)
})

function fileNameFromHeader(disposition) {
  let result = null;
  disposition = disposition.split(";")[1];
  if (disposition && /filename=.*/gi.test(disposition)) {
    result = disposition.match(/filename=.*/gi);
    return decodeURIComponent((result[0].split("=")[1]).replace(/\+/g, '%20'));
  }
  return "null";
}

4.其他问题

下载大文件过程中遇到的其他问题

  • axios请求超时,注意配置timeout
  • Nginx 响应超时报504 网关超时错误,注意配置Nginx
  • 控制台报error response,浏览器请求长时间得不到响应,本地调试代理转发超时造成的,参考
  • 文件超过2G的解决方案
07-03 09:12