记一次前端文件下载处理过程

Axios 请求拦截处理

TypeScript 复制代码
  // 响应拦截器
  instance.interceptors.response.use(async (response: AxiosResponse<customRes, any>): Promise<any> => {
    // 文件流处理
    if (response.config.responseType == "blob") return Promise.resolve(response);
  
    return Promise.resolve(response.data);
  }, responseErrorHandle)

当响应的数据类型是文件流(Blob)时,拦截器直接返回了一个包含 response 的 Promise,以便在调用这个接口的地方能够获取到文件流并进一步处理。

请求方法处理

通过 Axios 请求文件流(Blob)并进行文件下载的函数。

请求服务器返回的文件流数据,并通过浏览器的下载机制将其保存到本地文件。

设置响应类型为 Blob 是为了告知服务器返回的数据是二进制流,方便后续在客户端进行文件下载。

TypeScript 复制代码
  /**
   * 返回文件流测试
   */
  export const getFile = () => {
    instance.get('/test/file/stream', {
      responseType: "blob", // 设置响应类型为blob
      params: {}
    }).then((response) => {
      // 处理下载文件
      const url = window.URL.createObjectURL(new Blob([response.data]));
      const link = document.createElement("a");
      link.href = url;
      link.setAttribute("download", getFileNameFromHeaders(response)); // 设置下载的文件名和扩展名
      link.click();
    })
  }

从headers中取出文件名方法封装

TypeScript 复制代码
  // 文件名处理
  export const getFileNameFromHeaders = (response: AxiosResponse) => {
    // 从响应头中获取Content-Disposition信息
    const contentDisposition = response.headers['content-disposition'];
  
    // 使用正则表达式匹配文件名
    const match = /filename="(.*)"/.exec(contentDisposition);
  
    // 获取匹配的文件名
    return match ? decodeURI(match[1]) : 'unknown'
  }
  1. instance.get('/test/file/stream', {...})
    • 使用 Axios 的 get 方法向服务器端发送 GET 请求,请求的地址是 '/test/file/stream'。
  2. responseType: "blob"
    • 在请求配置中设置 responseType 为 "blob",表示期望服务器响应的数据是二进制数据(Blob 对象)。
  3. .then((response) => {...})
    • 使用 Promise 的 .then 处理请求成功的回调函数,response 包含了从服务器返回的 Blob 对象。
  4. window.URL.createObjectURL(new Blob([response.data]))
    • 通过 window.URL.createObjectURL 创建一个临时的 URL,该 URL 表示了从服务器返回的 Blob 对象的内容。这个 URL 通常用于创建下载链接。
  5. const link = document.createElement("a");
    • 创建一个 <a> 元素,用于模拟文件下载。
  6. link.href = url;
    • 将刚刚创建的 URL 赋值给 <a> 元素的 href 属性。
  7. link.setAttribute("download", getFileNameFromHeaders(response));
    • 设置 <a> 元素的 download 属性为从服务器响应头中获取的文件名。这里使用 getFileNameFromHeaders 的函数,它可能用于从响应头中获取文件名,确保文件名包含在 Content-Disposition 头中。
  8. link.click();
    • 模拟点击 <a> 元素,触发文件下载。

整个流程的目的是通过 Axios 请求获取服务器端返回的文件流,然后使用 Blob 对象创建一个临时的 URL,并通过创建 <a> 元素模拟点击以触发文件下载。

网络服务处理

返回文件流的接口

JavaScript 复制代码
  // 服务端返回文件流,并将文件名通过 attachment 返回
  router.get('/file/stream', async (ctx, next) => {
      const filePath = path.join(__dirname, '../upload/file/测试表格.xlsx');
      // 设置响应头,告诉浏览器文件名
      ctx.attachment(encodeURI('测试表格.xlsx'));
      // 读取文件流并设置为响应体
      ctx.body = await fs.createReadStream(filePath);
  })

跨域配置

允许前端访问 Content-Disposition

JavaScript 复制代码
app.use(cors({
    exposeHeaders: ['Content-Disposition'],
    credentials: true,  // 允许携带cookies
}));

总结

服务端返回文件流,并将文件名通过 attachment 返回,文件名会被携带在 Content-Disposition中。

前端通过 getFileNameFromHeaders 方法取出 文件名

前端通过 window.URL.createObjectURL(new Blob(response.data)) 创建一个可用于浏览器中的文件下载的 URL

相关推荐
苏灿烤鱼10 分钟前
没有机密文件,也没有付费数据,在浏览器里造一颗"间谍卫星"
前端·javascript·github
泯泷36 分钟前
手搓JSVM第 5 篇:写第一个编译器:从 AST 生成 IR
前端·javascript·算法
泯泷43 分钟前
手搓JSVM第 7 篇:控制流:if、while 与 jump
前端·javascript·算法
泯泷1 小时前
手搓JSVM第 6 篇:把 IR 编成字节码:emit 与 label fixup
前端·javascript·算法
泯泷1 小时前
手搓JSVM第 3 篇:从栈式 VM 到寄存器式 VM:为什么我们选择寄存器
前端·javascript·算法
泯泷1 小时前
第 4 篇:让 VM 支持变量:Slot、Environment 与 TDZ
前端·javascript·算法
泡海椒2 小时前
告别手写 HTTP 模板!JQuick-Curl:直接把 curl 命令跑在 Java 中
java·网络协议·http
粥里有勺糖3 小时前
视野修炼-技术周刊第131期 | Bun 与 pnpm Rust 化
前端·github·agent
Coodor3 小时前
如何在web浏览器使用js操作CPU卡
开发语言·前端·javascript·cpu卡