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

670 阅读3分钟

Axios 请求拦截处理

  // 响应拦截器
  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 是为了告知服务器返回的数据是二进制流,方便后续在客户端进行文件下载。

  /**
   * 返回文件流测试
   */
  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中取出文件名方法封装

  // 文件名处理
  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> 元素模拟点击以触发文件下载。

网络服务处理

返回文件流的接口

  // 服务端返回文件流,并将文件名通过 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

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

总结

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

image.png

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

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