切換語言為:簡體

前端檔案下載必備技巧:如何優雅處理後端流資料?

  • 爱糖宝
  • 2024-11-29
  • 2029
  • 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.