前端文件流下载方法封装

目录

在Web开发中,我们经常会遇到需要处理文件下载的情况。特别是在处理API响应时,有时我们需要根据响应的内容类型和状态码来决定如何处理数据。本文将详细解析一段用于处理文件下载的JavaScript代码,该代码优先尝试将响应解析为JSON错误信息,若解析失败则按正常文件进行下载。

代码详解

  1. 从API响应中读取数据。
  2. 尝试将响应内容解析为JSON格式,以判断是否为错误信息。
  3. 根据解析结果决定继续处理还是直接下载文件。
  4. 如果确定为文件,则创建Blob对象并触发浏览器下载。
javascript 复制代码
// 下载文件并处理响应:优先尝试解析为 JSON 错误信息,若解析失败则按正常文件下载
const downLoadFileBlob = (res, fileName, type = 'application/vnd.ms-excel;charset=utf-8', validateErrorCode) => {
  const reader = new FileReader()
  // 将响应数据读取为文本,以便后续尝试 JSON 解析
  reader.readAsText(res.data)
  reader.onload = () => {
    try {
      // 尝试将响应文本解析为 JSON,判断是否为错误响应
      const response = JSON.parse(reader.result)
      console.log(response)
      // 若外部传入自定义错误码校验函数且校验通过,则直接返回,不再处理
      if (validateErrorCode && validateErrorCode(response.errorCode)) return
      // 错误码 401 表示登录失效,清空本地缓存并跳转登录页
      if (response.errorCode === 401) {
        localStorage.clear()
        window.location.href = '/login'
        return
      }
      // 若响应头指示为 JSON 文件,则主动抛出异常,进入 catch 分支进行文件下载
      if (res.headers['content-disposition'].indexOf('.json') !== -1) {
        throw new Error('文件解析失败,直接下载')
      } else {
        // 其他情况弹窗提示接口错误
        alert('接口错误')
      }
    } catch (err) {
      // 捕获异常后,进入正常文件下载流程
      console.log('下载正常处理')
      var downFileName = fileName
      try {
        // 从响应头 content-disposition 中提取文件名并解码
        downFileName = res.headers['content-disposition'].split(';')[1].split('=')[1]
        downFileName = decodeURIComponent(downFileName)
      } catch {
        // 若提取失败,使用默认传入的文件名
        console.log('file name is empty')
      }
      // 获取文件扩展名,用于决定 Blob 的 MIME 类型
      let fileArr = downFileName.split('.')
      let extensionName = fileArr[fileArr.length - 1]
      let blob
      if (extensionName === 'zip') {
        // ZIP 文件使用 Excel MIME 类型(兼容旧逻辑)
        blob = new Blob([res.data], { type: 'application/vnd.ms-excel;charset=utf-8' })
      } else if (extensionName === 'json') {
        // JSON 文件使用 JSON MIME 类型
        blob = new Blob([res.data], { type: 'application/json;charset=utf-8' })
      } else {
        // 其他文件使用外部传入或默认 MIME 类型
        blob = new Blob([res.data], { type: type })
      }
      // 创建 Blob URL 并触发浏览器下载
      var url = window.URL.createObjectURL(blob)
      var aLink = document.createElement('a')
      aLink.style.display = 'none'
      aLink.href = url
      aLink.setAttribute('download', downFileName)
      document.body.appendChild(aLink)
      aLink.click()
      // 下载完成后移除临时元素并释放 Blob URL
      document.body.removeChild(aLink)
      window.URL.revokeObjectURL(url)
    }
  }
}

方法接收四个参数,分别如下

  • res: API响应对象,包含dataheaders等属性。
  • fileName: 默认文件名。
  • type: Blob对象的MIME类型,默认值为'application/vnd.ms-excel;charset=utf-8'
  • validateErrorCode: 自定义的错误码校验函数。

FileReader对象用于异步读取BlobFile对象的内容。这里我们将响应数据res.data作为文本读取,以便后续尝试将其解析为JSON,为什么要做这部操作?直接解析Blob不就行了吗?现实中肯定不会这里简单,假如点击下载时登录失效,这个时候返回的时{errorCode : 401, errorMsg: '登录失效'},这个时候再按照Blob解析就会报错,不会退出登录,如果提供了validateErrorCode函数,则需要在validateErrorCode中自己处理错误,如果不提供,可以在下面处理公共错误。

如果JSON解析失败,证明时数据流了(不是JSON或数据流自行处理),从Content-Disposition头部提取文件名,并进行URL解码,创建Blob对象,创建隐藏的<a>标签,设置其href为Blob URL,并通过click事件触发下载,下载完成后,移除临时创建的<a>标签,并释放Blob URL。

本地开发可以拿到Content-Disposition字段,部署之后拿不到怎么办?请移步另一篇博文,前端无法获取响应头(如 Content-Disposition)的原因与解决方案

  1. API封装
javascript 复制代码
export const downloadFile = params => {
  return request.post(`${process.env.REACT_APP_API_URL}/api`, params, {
    // hiddenError: true,
    // noSpin: true,
    responseType: 'blob',
    uploadOrDownload: true
  })
}
  1. API调用
javascript 复制代码
downloadFile(params)
  .then(res => {
  		// 后续会补充downLoadFileBlob方法
    downLoadFileBlob(res)
  })
相关推荐
我会一直在的2 小时前
Fiddler基础使用介绍
前端·测试工具·fiddler
IT_陈寒2 小时前
Vite 5大优化技巧:让你的构建速度飙升50%,开发者都在偷偷用!
前端·人工智能·后端
CodeCraft Studio2 小时前
Vaadin 25 正式发布:回归标准Java Web,让企业级开发更简单、更高效
java·开发语言·前端·vaadin·java web 框架·纯java前端框架·企业级java ui框架
Shirley~~2 小时前
PPTist 幻灯片工具栏Toolbar部分
开发语言·前端·javascript
|晴 天|2 小时前
Promise 与 async/await 错误处理最佳实践指南
开发语言·前端·javascript
vx_bisheyuange2 小时前
基于SpringBoot的便利店信息管理系统
前端·javascript·vue.js·毕业设计
晚烛2 小时前
智启工厂脉搏:基于 OpenHarmony + Flutter 的信创工业边缘智能平台构建实践
前端·javascript·flutter
Zsnoin能2 小时前
都快2026了,还有人不会国际化和暗黑主题适配吗,一篇文章彻底解决
前端·javascript
两个西柚呀2 小时前
es6和commonjs模块化规范的深入理解
前端·javascript·es6