JavaScript 與 Blob 下載檔案最佳實踐

在現代網頁開發中,下載檔案(如 PDF、圖片、Excel 等)時,常見的最佳做法如下:

1. 使用 fetch + response.blob()(自動解析 Content-Type)

fetch('<PDF URL>')
  .then(response => response.blob())
  .then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = '<PDF filename>.pdf';
    a.click();
    window.URL.revokeObjectURL(url);
  });
  • 優點:簡潔,fetch 會自動根據 HTTP Content-Type 產生正確的 Blob 物件。
  • 適用於大多數靜態檔案、API 回傳二進位內容。

2. 使用 fetch + response.arrayBuffer() + new Blob()(自訂 MIME 類型)

fetch('<PDF URL>')
  .then(response => response.arrayBuffer())
  .then(arrayBuffer => {
    const blob = new Blob([arrayBuffer], { type: 'application/pdf' });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = '<PDF filename>.pdf';
    a.click();
    window.URL.revokeObjectURL(url);
  });
  • 優點:可自訂 MIME 類型,適合 API 未正確回傳 Content-Type 時。
  • 適用於需強制指定檔案格式的場景。

3. 下載其他格式(如圖片、Excel、CSV)

只需將 type 改為對應格式即可:

const blob = new Blob([arrayBuffer], { type: 'image/png' });
// 或 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' (Excel)

4. 下載大檔案注意事項

  • 建議加上 loading UI,避免用戶誤以為無反應。
  • 可用 async/await 寫法提升可讀性。
  • 下載完成後務必釋放 URL,避免記憶體洩漏。

5. 進階:支援 IE11(舊瀏覽器)

if (window.navigator && window.navigator.msSaveOrOpenBlob) {
  window.navigator.msSaveOrOpenBlob(blob, '<PDF filename>.pdf');
} else {
  // ...如上 createObjectURL 寫法
}

常見問題(FAQ)

Q1:為什麼要手動釋放 createObjectURL? A:每次呼叫 createObjectURL 都會佔用瀏覽器記憶體,若未釋放,長時間操作會造成記憶體洩漏。

Q2:如何動態產生檔名? A:可根據 API 回傳 header 或自訂規則設定 a.download,例如:

a.download = `report_${Date.now()}.pdf`;

Q3:下載後檔案損毀? A:請確認 fetch 沒有被 CORS、權限或 Content-Type 設定影響,並確保 arrayBuffer/Blob 內容正確。

Q4:如何下載非 PDF 檔案? A:只需調整 Blob 的 type 參數與下載副檔名即可。


延伸閱讀: