切换语言为:繁体

前端文件下载必备技巧:如何优雅处理后端流数据?

  • 爱糖宝
  • 2024-11-29
  • 2030
  • 0
  • 0

文件下载场景

在前端开发中,通过某个接口下载文件是一种非常常见的需求。如果后端返回的是一个文件地址,那我们使用a标签就能很轻松实现文件的下载。

function downloadFileFromUrl(url, fileName) {
  const link = document.createElement('a'); // 创建 <a> 元素
  link.href = url; // 设置文件地址
  link.download = fileName  // 设置文件名(
  link.click(); // 触发点击,自动下载
}

但是,如果后端返回的是一个数据流,我们的处理就相对复杂些。

如图,点击导出的时候,后端返回的是一个流文件

前端文件下载必备技巧:如何优雅处理后端流数据?

这个流文件可能是Excel表格、也可能是PDF文件图片,并且这个流文件也可能包含了文件的名称信息。那么,前端应该如何处理接口返回的数据,正确将流数据下载成文件呢

文件下载的核心原理

当后端通过流的形式返回文件内容时,要想实现文件的下载,我们的大致思路和过程如下:

  1. 通过axios或者浏览器原生的fecth方法调用接口接收流数据。

  2. 转换流数据为 Blob 对象。

Blob 代表“二进制大对象”, 可以作为文本或二进制数据被读取。但要注意,Blob 表示的不一定是 JavaScript 原生格式的数据,而是计算机界的通用术语。

  1. 利用 URL.createObjectURL 方法生成文件的临时链接。

  2. 使用 <a> 标签触发文件下载。

接下来,我们分别使用 AxiosFetch 进行实现。

使用 Axios 实现文件下载

Axios 提供了强大的功能,可以轻松处理流数据,但需要进行一些配置。

import axios from 'axios';

function downloadFileWithAxios() {
  axios.post('/api/download',{},{
    responseType: 'blob', // 确保接收到的是二进制流
  }).then((response) => {
      const blob = new Blob([response.data]); // 将响应数据转为 Blob
      const downloadUrl = URL.createObjectURL(blob); // 创建临时链接
    
      const link = document.createElement('a'); // 创建 <a> 元素
      link.href = downloadUrl;
      link.download = '自定义文件名.avi'; // 自定义文件名
      link.click(); // 触发下载

      URL.revokeObjectURL(downloadUrl); // 释放 URL
    })
    .catch((error) => {
      console.error('文件下载失败:', error);
    });
}

通过上述代码,我们借助axios实现了流数据的下载。

其中,设置 responseType 为 blob 是能否成功的关键,否则 Axios 默认将数据解析为 JSON,我们的下载就会失败。

上述代码中,我们自定义了文件名,如果后端设置了文件名,我们可以从响应头中读取并动态设置文件名:

// ......

// 动态获取文件名
const contentDisposition = response.headers['content-disposition'];
const fileName = getFileNameFromDisposition(contentDisposition) || '自定义文件名';

// ......

// 从 Content-Disposition 提取文件名
function getFileNameFromDisposition(disposition) {
  if (!disposition) return null;
  const match = disposition.match(/filename*?=(?:UTF-8'')?([^;]+)/i);
  return match ? decodeURIComponent(match[1].replace(/^"|"$/g, '')) : null;
}

使用原生 Fetch 实现文件下载

和Axios的实现原理一致,原生 Fetch 的灵活性更高,但处理起来需要更多的代码。

async function download() {
  try {
    const response = await fetch('/api/download', { method: 'GET' });

    if (!response.ok) {
      throw new Error('网络响应失败');
    }

    const blob = await response.blob(); // 将响应数据转为 Blob
    const downloadUrl = URL.createObjectURL(blob); // 创建临时链接

    const link = document.createElement('a'); // 创建 <a> 元素
    link.href = downloadUrl;
    link.download = '自定义文件名.avi';
    link.click(); // 触发下载

    URL.revokeObjectURL(downloadUrl); // 释放 URL
  } catch (error) {
    console.error('文件下载失败:', error);
  }
}

同样的,如果后端设置了文件名,我们需要从Content-Disposition 中获取文件名

async function download() {
  try {
    const response = await fetch('/api/download', { method: 'GET' });

    if (!response.ok) {
      throw new Error('网络响应失败');
    }

  // 获取 Content-Disposition 头部并提取文件名
  + const contentDisposition = response.headers.get('Content-Disposition');
  + const fileName = getFileNameFromDisposition(contentDisposition)
    
    const blob = await response.blob(); // 将响应数据转为 Blob
    const downloadUrl = URL.createObjectURL(blob); // 创建临时链接

    const link = document.createElement('a'); // 创建 <a> 元素
    link.href = downloadUrl;
  + link.download = fileName; 
    link.click(); // 触发下载

    URL.revokeObjectURL(downloadUrl); // 释放 URL
  } catch (error) {
    console.error('文件下载失败:', error);
  }
}

// 从 Content-Disposition 提取文件名
function getFileNameFromDisposition(disposition) {
  if (!disposition) return null;
  const match = disposition.match(/filename*?=(?:UTF-8'')?([^;]+)/i);
  return match ? decodeURIComponent(match[1].replace(/^"|"$/g, '')) : null;
}

BlobURL.createObjectURL 解析

无论使用Axios还是Fecth,流文件下载功能的核心都是下面两句代码:

const blob = new Blob([response.data]); // 将响应数据转为 Blob
const downloadUrl = URL.createObjectURL(blob); // 创建临时链接

它们的作用是将服务器返回的数据处理成可以在浏览器中直接下载的文件形式,现在,我们简要分析下着这两段代码。

new Blob()的作用

Blob 是浏览器提供的一种二进制数据的封装对象,它可以存储二进制数据,例如文件内容、图像、视频或任意字节序列。

它的语法如下:

const blob = new Blob(array, options);
  • array:包含要存储在 Blob 中的数据,可以是字符串、数组缓冲区或其他 Blob 对象。

  • options:可选参数对象,包含:

    • type:MIME 类型(如 "application/pdf""image/png" 等)。通过 options 指定 MIME 类型,浏览器能正确识别文件格式。

    • endings:表示字符串中换行符的处理方式。

new Blob([response.data]) 这段代码主要作用是将响应数据转换为文件:response.data 是服务器返回的原始数据(例如 ArrayBufferUint8Array),通过 Blob 封装后,它就变成一个可以表示文件的对象。

浏览器在处理时会根据内容自动推断文件类型,因此new Blob的第二个参数我们可以不传。

URL.createObjectURL(blob) 的作用

URL.createObjectURL 方法会为传入的 BlobFile 对象创建一个临时的 URL,浏览器可以通过这个 URL 直接访问 Blob 数据,而无需上传到服务器。

它的语法如下:

const url = URL.createObjectURL(object);
  • object:必须是一个 Blob 或 File 对象。

  • 返回值:一个字符串形式的临时 URL(如 blob:http://shi.xiaoshiexample.com/)。

在文件下载中,通过临时 URL,我们将可以 Bl将ob 数据转化为一个可被浏览器访问的地址,然后就可以利用 <a> 标签的 hrefdownload 属性实现文件下载。

总结

通过本文,相信大家对前端开发中流数据的下载已经掌握了。我们在简单复习一下核心的实现过程:通过使用 Axios 或 Fetch,我们可以接收文件流,并将其转为 Blob 对象,之后通过 URL.createObjectURL 创建一个临时链接。利用 <a> 标签的 hrefdownload 属性,我们可以触发文件的自动下载。如果后端提供了文件名,我们可以从响应头的 Content-Disposition 中提取文件名。

总之,关键步骤是将流数据转化为 Blob,再生成下载链接,从而实现文件下载功能。

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.