當后端返回一個 .xlsx
文件流時,前端可以通過 JavaScript 處理這個文件流并觸發瀏覽器下載。
實現步驟
-
發送請求獲取文件流:
使用fetch
或axios
等工具向后端發送請求,確保響應類型設置為blob
(二進制數據流)。 -
創建 Blob 對象:
將返回的文件流轉換為Blob
對象,這是處理二進制數據的標準方式。 -
生成下載鏈接:
使用URL.createObjectURL
方法將Blob
對象轉換為一個臨時 URL。 -
觸發下載:
動態創建一個<a>
標簽,設置其href
屬性為生成的臨時 URL,并調用click()
方法觸發下載。 -
清理資源:
下載完成后,使用URL.revokeObjectURL
釋放生成的臨時 URL,避免內存泄漏。
使用 axios
實現
如果你使用的是 axios
,可以這樣實現:
import axios from 'axios';const downloadXlsx = async () => {try {// 發送請求并獲取文件流const response = await axios.get('/api/download-xlsx', {responseType: 'blob', // 確保響應類型為 blobheaders: {'Authorization': 'Bearer your_token_here', // 如果需要攜帶認證信息},});// 創建 Blob 對象const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });// 創建臨時 URLconst url = window.URL.createObjectURL(blob);// 創建 <a> 標簽并觸發下載const a = document.createElement('a');a.href = url;a.download = 'example.xlsx'; // 設置下載文件名document.body.appendChild(a); // 將 <a> 添加到 DOM 中(可選)a.click(); // 觸發點擊事件// 清理資源window.URL.revokeObjectURL(url);a.remove(); // 移除 <a> 標簽} catch (error) {console.error('下載文件時發生錯誤:', error.message || '未知錯誤');}
};// 調用函數
downloadXlsx();
responseType: 'blob'
?的含義
responseType
?是 Axios 提供的一個配置選項,用于告訴 Axios 如何解析服務器返回的數據。- 當設置為?
'blob'
?時,表示你期望服務器返回的數據是一個二進制大對象(Binary Large Object,簡稱 Blob)。
Blob 是什么?
- Blob?是一種數據類型,通常用來表示不可變的、原始數據的類文件對象。
- 它可以包含文本、圖像、音頻、視頻等二進制數據,或者混合內容。
- 在前端開發中,Blob 常用于處理文件下載、圖片預覽、或者將數據保存為文件。
為什么需要?responseType: 'blob'
?
當從服務器請求文件(如 Excel 文件、PDF 文件、圖片等)時,服務器通常會返回二進制數據。如果直接使用默認的 responseType
(通常是 'json'
),Axios 會嘗試將響應數據解析為 JSON 格式,這會導致錯誤或數據損壞。
通過設置 responseType: 'blob'
,你可以確保 Axios 正確地將響應數據作為二進制數據處理,而不會嘗試將其解析為其他格式(如 JSON 或文本)。
代碼的作用
const response = await axios({url: '/api/download-xlsx',method: 'GET',responseType: 'blob', // 指定響應數據為 Blob 類型
});
-
發起 GET 請求:
- 向?
/api/download-xlsx
?發起一個 GET 請求,通常用于下載文件(例如 Excel 文件)。
- 向?
-
指定響應類型:
- 設置?
responseType: 'blob'
,告訴 Axios 將服務器返回的數據解析為 Blob 對象。
- 設置?
-
處理響應數據:
- 返回的?
response.data
?將是一個 Blob 對象,代表下載的文件內容。
- 返回的?
使用 fetch
實現
const downloadXlsx = async () => {try {// 發送請求并獲取文件流const response = await fetch('/api/download-xlsx', {method: 'GET',headers: {'Authorization': 'Bearer your_token_here', // 如果需要攜帶認證信息},});// 檢查響應是否成功if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}// 獲取 blob 數據const blob = await response.blob();// 創建臨時 URLconst url = window.URL.createObjectURL(blob);// 創建 <a> 標簽并觸發下載const a = document.createElement('a');a.href = url;a.download = 'example.xlsx'; // 設置下載文件名document.body.appendChild(a); // 將 <a> 添加到 DOM 中(可選)a.click(); // 觸發點擊事件// 清理資源window.URL.revokeObjectURL(url);a.remove(); // 移除 <a> 標簽} catch (error) {console.error('下載文件時發生錯誤:', error.message || '未知錯誤');}
};// 調用函數
downloadXlsx();
1. 定義異步函數
javascript
深色版本
const downloadXlsx = async () => {
async
?關鍵字表示這是一個異步函數。- 異步函數允許使用?
await
?來等待異步操作(如網絡請求)完成。
2. 發起 HTTP 請求
const response = await fetch('/api/download-xlsx', {method: 'GET',headers: {'Authorization': 'Bearer your_token_here', // 如果需要攜帶認證信息},
});
-
使用?
fetch
?發起一個 GET 請求到?/api/download-xlsx
。 -
headers
?部分用于設置請求頭:'Authorization'
?是一個常見的請求頭字段,用于傳遞用戶的身份驗證信息(如 JWT Token)。- 如果 API 不需要認證,可以省略?
headers
。
3. 檢查響應狀態
if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);
}
response.ok
?是一個布爾值,表示 HTTP 響應的狀態碼是否在 200-299 范圍內。- 如果響應失敗(如 404 或 500 錯誤),會拋出一個錯誤,并終止后續代碼執行。
- 拋出的錯誤會被?
catch
?塊捕獲。
4. 獲取 Blob 數據
const blob = await response.blob();
response.blob()
?將響應數據解析為一個?Blob
?對象。- Blob?是一種二進制數據類型,通常用來表示文件內容。
await
?確保在繼續執行后續代碼之前,blob
?數據已經被完全加載。
5. 創建臨時 URL
const url = window.URL.createObjectURL(blob);
window.URL.createObjectURL(blob)
?創建一個指向?Blob
?的臨時 URL。- 這個 URL 可以被用作?
<a>
?標簽的?href
?屬性,從而觸發文件下載。
6. 創建?<a>
?標簽并觸發下載
const a = document.createElement('a');
a.href = url;
a.download = 'example.xlsx'; // 設置下載文件名
document.body.appendChild(a); // 將 <a> 添加到 DOM 中(可選)
a.click(); // 觸發點擊事件
-
創建?
<a>
?標簽:- 使用?
document.createElement('a')
?動態創建一個?<a>
?元素。
- 使用?
-
設置?
<a>
?標簽屬性:href
:設置為前面生成的臨時 URL。download
:指定下載文件的名稱(如?example.xlsx
)。
-
添加到 DOM 并觸發點擊:
- 將?
<a>
?標簽添加到文檔中(雖然不是必須,但某些瀏覽器可能需要這樣做)。 - 使用?
a.click()
?模擬用戶點擊,從而觸發文件下載。
- 將?
7. 清理資源
window.URL.revokeObjectURL(url);
a.remove();
-
釋放臨時 URL:
- 使用?
window.URL.revokeObjectURL(url)
?釋放前面創建的臨時 URL,避免內存泄漏。
- 使用?
-
移除?
<a>
?標簽:- 使用?
a.remove()
?從 DOM 中移除?<a>
?標簽,保持頁面整潔。
- 使用?
8. 錯誤處理
} catch (error) {console.error('下載文件時發生錯誤:', error.message || '未知錯誤');
}
catch
?塊?用于捕獲和處理可能出現的錯誤。- 如果在?
try
?塊中的任何一步發生錯誤(如網絡問題、API 返回錯誤等),都會進入?catch
?塊。 - 使用?
console.error
?打印錯誤信息,方便調試。
9. 調用函數
downloadXlsx();
- 最后調用?
downloadXlsx
?函數,觸發整個文件下載流程。
適用場景
- 下載動態生成的文件(如 Excel 表格、PDF 文檔等)。
- 需要通過 API 獲取文件內容并提供給用戶下載的場景。
注意事項
-
跨域問題:
- 如果 API 存在跨域限制,需要確保服務器配置了正確的 CORS(跨域資源共享)策略。
-
大文件下載:
- 對于非常大的文件,可能需要考慮分塊下載或流式處理,以避免內存占用過高。
-
瀏覽器兼容性:
fetch
?和?Blob
?在現代瀏覽器中廣泛支持,但在一些老舊瀏覽器中可能需要使用?XMLHttpRequest
?替代。
關鍵點說明
-
設置正確的 MIME 類型:
.xlsx
文件的 MIME 類型是application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
。- 如果后端未正確設置 MIME 類型,前端可以通過
Blob
的第二個參數手動指定。
-
動態文件名:
- 如果后端在響應頭中提供了文件名(例如通過
Content-Disposition
),可以通過解析響應頭來動態設置文件名。 - 示例:
const contentDisposition = response.headers['content-disposition']; let fileName = 'example.xlsx'; if (contentDisposition && contentDisposition.includes('filename=')) {fileName = contentDisposition.split('filename=')[1].split(';')[0]; }
- 如果后端在響應頭中提供了文件名(例如通過
-
錯誤處理:
- 在實際開發中,務必對請求錯誤進行處理,例如檢查 HTTP 狀態碼是否為 200,或者捕獲網絡異常。
-
兼容性:
- 上述方法適用于現代瀏覽器(如 Chrome、Firefox、Edge、Safari)。如果需要支持舊版瀏覽器,可能需要引入
FileSaver.js
庫。
- 上述方法適用于現代瀏覽器(如 Chrome、Firefox、Edge、Safari)。如果需要支持舊版瀏覽器,可能需要引入
使用 FileSaver.js 簡化操作
如果你希望簡化文件下載邏輯,可以使用第三方庫 file-saver
。安裝后,只需幾行代碼即可完成下載。
安裝
npm install file-saver
使用示例
import { saveAs } from 'file-saver';
import axios from 'axios';const downloadXlsx = async () => {try {const response = await axios({url: '/api/download-xlsx',method: 'GET',responseType: 'blob',});// 使用 FileSaver.js 保存文件saveAs(response.data, 'example.xlsx');} catch (error) {console.error('下載失敗:', error);}
};// 調用函數
downloadXlsx();
總結
- 核心步驟:獲取文件流 -> 轉換為 Blob -> 創建臨時 URL -> 觸發下載 -> 清理資源。
- 推薦工具:
fetch
或axios
可以輕松獲取文件流,FileSaver.js
可以進一步簡化代碼。 - 注意事項:確保后端返回的文件流格式正確,前端設置合適的 MIME 類型和文件名。
axios 處理
try {const response = await axios({url: '/api/download-xlsx',method: 'GET',responseType: 'blob', // 一定要注意請求時加此方法});getExportListApi(exportCarryData).then((res) => {// 創建一個a標簽const link = document.createElement('a');const url = window.URL.create0bjectURL(new Blob([res.data])); link.href = url;link.setAttribute('download', exportfile);document.body.appendChild(link); link.click();// 銷毀a標簽document.body.removeChild(link); window.URL.revoke0bjectURL(url);message.success('導出成功!');});