在前端開發中,有時需要從後端提供數據並生成文件提供用戶下載。以下將以下載 Excel 為例,介绍如何使用 axios 從後端取得文件,並在前端處理文件的下載過程。我们還會顯示如何從 HTTP header 中提取文件名,以 f 確保下載的文件命名正確。
Api 設定
要記得設置告诉 axios 期望接收的類型為 blob,若是不設置,則會預設為 json,收到的資料會是亂碼。
1 2 3 4 5 6 7 8
| exportFile(searchData) { return axios.get("/data/export", { params: searchData, responseType: "blob" }); }
|
前端處理文件下載
獲取到後端返回的文件數據後,我們需要在前端將其轉換為可下載的文件格式。以下是一個完整的示例,展示瞭如何將 blob 數據生成 Excel 文件並觸發下載。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const handleExportFile = async () => { try { const res = await exportFile(data);
const blob = new Blob([res.data], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", });
const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = downloadUrl;
const fileName = "export.xlsx"; link.setAttribute("download", fileName); document.body.appendChild(link); link.click(); link.remove();
window.URL.revokeObjectURL(downloadUrl); } catch (error) { console.error(error); } };
|
new Blob([res.data], { type: … }) 做了什麼?
這里,用 new Blob() 創建了一個 Blob 對象。
res.data 是從後端 API 接收到的文件數據。
我們將這個數據放入 Blob 中,同時指定了文件的類型,即 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
,這是 Excel 文件的 MIME 類型。這樣瀏覽器就能識別出這是一個 Excel 文件。
動態創建一個 <a>
標簽,並通過模擬點擊觸發文件下載。
使用 window.URL.createObjectURL(blob) 創建了一個特殊的 URL,它指向我們剛才創建的 Blob 對象。
可以理解為它生成了一個臨時的下載地址,使用者可以透過這個地址下載文件。
接著 document.createElement(“a”) 創建 <a>
標籤,接下來要利用這個標籤來實現文件下載。
也就是 link.href = downloadUrl; 給 <a>
標籤賦值,將剛才生成的 downloadUrl 設定為這個 <a>
標籤的 link 地址 (href),也就是說,點擊這個 link 會指向我們的 Blob 物件(即文件資料)。
設定文件名並觸發下載
使用 setAttribute 方法給<a>
標籤添加一個 download 屬性,並設定文件名為 “export.xlsx”。這樣,當用戶點擊鏈接時,瀏覽器會提示下載文件,並自動將文件保存為 export.xlsx。
document.body.appendChild 將 <a>
標籤臨時加到 document 的 body 中。雖然使用者不會看到這個 link,但它在頁面上是存在的。
然後模擬一次使用者點擊這個連結 (link.click()),觸發文件的下載過程。
link.remove();下載操作完成後,我們把這個臨時創建的 <a>
標籤從頁面中移除。
有時,後端會通過 Content-Disposition 頭信息傳遞文件名。為了確保下載時使用正確的文件名,我們可以從響應頭中解析文件名。
這時候要注意前端, axios 是如何處理 response (你能拿到的回傳內容是否有無 header)
需要從回傳的 response headers 取得檔名,這樣才能下載時有正確的檔名。
1 2 3 4 5 6 7 8
| headers:{ "access-control-allow-origin": "http://localhost:5143", "access-control-expose-headers": "*", "content-disposition": "attachment; filename=123123_202408.xlsx; filename*=UTF-8''123123_202408.xlsx", "content-length": "12111", "content-type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ... }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| const handleExportFile = async () => { try { $q.loading.show(); const data = { sourceId: Number(route.params.id), month: followQuery.month, }; const res = await getFollowMonthlySummaryDownload(data); $q.loading.hide();
const blob = new Blob([res.data], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", });
const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = downloadUrl;
const contentDisposition = res.headers["content-disposition"]; let fileName = "default-filename.xlsx";
if (contentDisposition) { const fileNameMatch = contentDisposition.match( /filename\*=UTF-8''([^;]+)/ ); if (fileNameMatch) { fileName = decodeURIComponent(fileNameMatch[1]); } else { const fileNameSimpleMatch = contentDisposition.match(/filename=([^;]+)/); if (fileNameSimpleMatch) { fileName = fileNameSimpleMatch[1].trim(); } } } link.setAttribute("download", fileName); document.body.appendChild(link); link.click(); link.remove();
window.URL.revokeObjectURL(downloadUrl); } catch (error) { console.error(error); } };
|