下载文件流处理

304 阅读5分钟

下载

一. 下载固定文档

固定文档后端通常会返回一个文档链接,可以使用 window.open() 直接下载;

二. 下载动态文档

  1. 有时是动态文档,此时后端会返回一个文件流,此时前端需要做一些特殊处理进行下载并获取文件名。
// 处理模版文件流下载
const downloading = ref(false);
const downloadFile = async () => {
  const res: any = await api({
    id: id.value,
    name: name.value,
  });
  try {
    const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const objectUrl = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = objectUrl;
    a.download = '这是一个固定的文件名';
    document.body.appendChild(a); // 部分浏览器要求a元素添加在DOM中才能正常工作
    a.click();
    URL.revokeObjectURL(objectUrl);
    document.body.removeChild(a);
  } catch (e: any) {
    LiMessage.error('下载失败,请联系管理员。报错信息:', e);
  }
};
  1. 获取动态文件名后端会在content-disposition中返回,此时需要单独处理,如果使用的是非最新脚手架,response会被封装起来,需要在request.ts的拦截器中放开

注意: 如果想在浏览器中通过 JavaScript 获取 `Content-Disposition` 字段,服务器需要在响应中设置`Access-Control-Expose-Headers` 响应头,并包括 `Content-Disposition` ,这样前端代码才能访问到它。 例如,后端可以设置以下的响应头: Access-Control-Expose-Headers: Content-Disposition

  // request.ts response 拦截器
  request.interceptors.response.use(
    (response) => {
      if (response.config.rawResponse) return response;
      return response.data;   //返回请求头信息
    },
    (error) => {
      if (env !== 'prod') {
        console.error('[Response Interceptors Error]: ', error); // for debug
      }
      switch (error.response.status) {
        case 401:
          break;
        case 400: // 如果响应中的 http code 为 400,那么就弹出一条错误提示给用户
        case 500:
          return LiMessage({
            message: error.response.data.message,
            type: 'error',
            duration: 1000,
          });
        default:
      }
      return Promise.reject(error);
    },
  );

接口api特殊处理

export function getApi(
  query: Cli.Request.query,
  configs?: RequestConfig,
  isMock?: boolean,
): RequestPromise<Cli.Response.type> {
  return request.get(
    `${ apiPrefix.api}/api`,
    {
      ...configs,
      params: query,
      responseType: 'blob',
      rawResponse: true,   
    },
  );
}
  1. 在response.header中获取文件名并处理

如果文件名类似: "Excel-ÀíÏëLÕýʽ°æ-Ä£°æ-20240110095859.xlsx" 前端将无法进行解码获取到正确文件名,这通常是因为前后端字符编码不一致,需要后端用url转码处理一下,此时获取到的文件名为filename="Excel-%E7%90%86%E6%83%B3L9%E6%AD%A3%%89%88SKU%E6%A8%A1%E7%89%88-20240110103630.xlsx";之后就可以进行专门解析

try { 
    const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); 
    const objectUrl = URL.createObjectURL(blob); 
    const a = document.createElement('a'); 
    a.href = objectUrl; 
    // 解析文件名,假设contentDisposition是响应头中获取到的Content-Disposition 
    let contentDisposition = res.headers['content-disposition']; 
    let fileName = contentDisposition.match(/filename\*?=(UTF-8'')?([^;]+)/)[2]; 
    if (fileName.startsWith('"') && fileName.endsWith('"')) { fileName = fileName.substring(1, fileName.length - 1); // 去掉两端的双引号 
    } 
    fileName = decodeURIComponent(fileName); // 对文件名进行解码 
    a.download = fileName; document.body.appendChild(a); // 部分浏览器要求a元素添加在DOM中才能正常工作 a.click(); URL.revokeObjectURL(objectUrl); document.body.removeChild(a); 
} catch (e: any) { 
    LiMessage.error('下载失败,请联系管理员。报错信息:', e); 
}

附录

MIME类型汇总:常见 MIME 类型列表 - HTTP | MDN

两种主要的 MIME 类型在默认类型中扮演了重要的角色:

  • text/plain 表示文本文件的默认值。一个文本文件应当是人类可读的,并且不包含二进制数据。
  • application/octet-stream 表示所有其他情况的默认值。一种未知的文件类型应当使用此类型。浏览器在处理这些文件时会特别小心,试图防止、避免用户的危险行为。

IANA 是 MIME 媒体类型的官方注册机构,并维护了官方所有 MIME 类型的列表。下面的表格列出了 Web 上的一些重要 MIME 类型:

扩展名文档类型MIME 类型
.aacAAC 音频audio/aac
.abwAbiWord 文档application/x-abiword
.apng动态可移植网络图形(APNG)图像image/apng
.arc归档文件(嵌入多个文件)application/x-freearc
.avifAVIF 图像image/avif
.aviAVI:音频视频交织文件格式(Audio Video Interleave)video/x-msvideo
.azwAmazon Kindle 电子书格式application/vnd.amazon.ebook
.bin任何二进制数据类型application/octet-stream
.bmpWindows OS/2 位图image/bmp
.bzBZip 归档application/x-bzip
.bz2BZip2 归档application/x-bzip2
.cdaCD 音频application/x-cdf
.cshC-Shell 脚本application/x-csh
.css层叠样式表(CSS)text/css
.csv逗号分隔值(CSV)text/csv
.docMicrosoft Wordapplication/msword
.docxMicrosoft Word(OpenXML)application/vnd.openxmlformats-officedocument.wordprocessingml.document
.eotMS 嵌入式 OpenType 字体application/vnd.ms-fontobject
.epub电子出版(EPUB)application/epub+zip
.gzGZip 压缩归档application/gzip
.gif图像互换格式(GIF)image/gif
.htm, .html超文本标记语言(HTML)text/html
.ico图标(Icon)格式image/vnd.microsoft.icon
.icsiCalendar 格式text/calendar
.jarJava 归档(JAR)application/java-archive
.jpeg, .jpgJPEG 图像image/jpeg
.jsJavaScripttext/javascript (规范:HTMLRFC 9239
.jsonJSON 格式application/json
.jsonldJSON-LD 格式application/ld+json
.mid, .midi音乐数字接口(MIDI)audio/midi、audio/x-midi
.mjsJavaScript 模块text/javascript
.mp3MP3 音频audio/mpeg
.mp4MP4 视频video/mp4
.mpegMPEG 视频video/mpeg
.mpkgApple 安装包application/vnd.apple.installer+xml
.odp开放文档演示稿文档application/vnd.oasis.opendocument.presentation
.ods开放文档表格文档application/vnd.oasis.opendocument.spreadsheet
.odt开放文档文本文档application/vnd.oasis.opendocument.text
.ogaOGG 音频audio/ogg
.ogvOGG 视频video/ogg
.ogxOGGapplication/ogg
.opusOpus 音频audio/opus
.otfOpenType 字体font/otf
.png便携式网络图形image/png
.pdfAdobe 便携式文档格式(PDF)application/pdf
.php超文本预处理器(Personal Home Page)application/x-httpd-php
.pptMicrosoft PowerPointapplication/vnd.ms-powerpoint
.pptxMicrosoft PowerPoint(OpenXML)application/vnd.openxmlformats-officedocument.presentationml.presentation
.rarRAR 归档application/vnd.rar
.rtf富文本格式(RTF)application/rtf
.sh伯恩 shell 脚本application/x-sh
.svg可缩放矢量图形(SVG)image/svg+xml
.tar磁带归档(TAR)application/x-tar
.tif, .tiff标签图像文件格式(TIFF)image/tiff
.tsMPEG 传输流video/mp2t
.ttfTrueType 字体font/ttf
.txt文本(通常是 ASCII 或 ISO 8859-n)text/plain
.vsdMicrosoft Visioapplication/vnd.visio
.wav波形音频格式audio/wav
.webaWEBM 音频audio/webm
.webmWEBM 视频video/webm
.webpWEBP 图像image/webp
.woffWeb 开放字体格式(WOFF)font/woff
.woff2Web 开放字体格式(WOFF)font/woff2
.xhtmlXHTMLapplication/xhtml+xml
.xlsMicrosoft Excelapplication/vnd.ms-excel
.xlsxMicrosoft Excel(OpenXML)application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.xmlXMLRFC 7303(section 4.1)推荐使用 application/xml,但有时仍会使用 text/xml。你可以将特定的 MIME 类型分配给具有 .xml 扩展名的文件,这取决于其内容的解释方式。例如,Atom 消息来源是 application/atom+xml,而 application/xml 是默认的有效值。
.xulXULapplication/vnd.mozilla.xul+xml
.zipZIP 归档application/zip
.3gp3GPP 音视频容器video/3gpp;如果不包含视频则为 audio/3gpp
.3g23GPP2 音视频容器video/3gpp2;如果不包含视频则为 audio/3gpp2
.7z7-zip 归档application/x-7z-compressed