業務需要導出表格數據,后端返回的是二進制數據流,使用axios請求,在前端接收到的是如下:
這就是二進制數據流,看不懂不要緊,前端只要處理如何下載就行了,下面就貼一下具體的代碼實現。
axios封裝:
import axios from "axios"; import { Loading, Message } from "element-ui"; import store from "@/store"; // import { getToken } from "@/utils/auth"; // create an axios instance const service = axios.create({ baseURL: "http://192.xxx.x.xx:8700", timeout: 10000, // request timeout // headers: { // "Content-Type": "multipart/form-data", // }, }); let apiCallNo = 0; let loadingInstance; // request interceptor // TODO 待優化 service.interceptors.request.use( (config) => { if (config.data) { const { hideLoading, ...rest } = config.data; if (!hideLoading) { apiCallNo += 1; if (apiCallNo === 1) { loadingInstance = Loading.service(); } } if (Object.keys(rest).length !== 0) { config.data = rest; } else if (typeof hideLoading === "boolean") { config.data = null; } } else { apiCallNo += 1; if (apiCallNo === 1) { loadingInstance = Loading.service(); } } if (store.getters.token) { // let each request carry token // ['X-Token'] is a custom headers key // please modify it according to the actual situation // config.headers["X-Token"] = getToken(); } return config; }, (error) => { // do something with request error return Promise.reject(error); } ); // response interceptor service.interceptors.response.use( (response) => { apiCallNo -= 1; if (apiCallNo === 0) { loadingInstance.close(); } const res = response.data; // 導出二進制流數據 if (res.type) { return res; } if (res.status !== 200) { Message({ message: res.message || "Error", type: "error", duration: 5 * 1000, }); return Promise.reject(new Error(res.message || "Error")); } else { return res.data; } }, (error) => { console.log(error.response); apiCallNo -= 1; if (apiCallNo === 0) { loadingInstance.close(); } Message({ message: error.response?.data.message ?? "網絡異常,請重試", // 統一的提示 type: "error", duration: 5 * 1000, }); return Promise.reject(error); } ); export default service;
二進制數據流返回的結果會有type屬性,但是沒有status這些屬性,根據這個將普通數據和二進制分開。
axios接口封裝:
export function exportList(data) { return request({ url: "/api/demo/result/exportResult", method: "post", responseType: "blob",
data, }); }
最重要的是 responseType: "blob", 這句代碼會將后端傳過來的數據流格式化成二進制如下:
導出方法: exportData
exportData() { if (this.multipleSelection.length === 0) { this.$message.error("請選擇要導出的數據"); return false; } this.exportList({ id: this.multipleSelection.join(","), }); // 清除
this.multipleSelection.length = 0; }, async exportList(reqData) { const res = await exportList(reqData); console.log("export:", res); const blob = new Blob([res], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8", }); const aEle = document.createElement("a"); // 創建a標簽 const href = window.URL.createObjectURL(blob); // 創建下載的鏈接 aEle.href = href; aEle.download = "信用評價模型數據.xlsx"; // 下載后文件名 document.body.appendChild(aEle); aEle.click(); // 點擊下載 document.body.removeChild(aEle); // 下載完成移除元素 window.URL.revokeObjectURL(href); // 釋放掉blob對象
},
上面的寫法就是用從服務器接收到的文件流(content-type:application/octet-stream)創建了一個blob對象,並使用該blob 創建一個指向類型數組的URL,將該url作為a標簽的鏈接目標,然后去觸發a標簽的點擊事件從而實現表格下載。
注意:上面在封裝請求方法的時候已經將響應類型resposeType設置成了blob對象,所以在上面的請求函數中可以不加new Blob()這個步驟,加上就有點重復了。
async exportList(reqData) { const res = await exportList(reqData); console.log("export:", res); // const blob = new Blob([res], { // type: // "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8", // }); const aEle = document.createElement("a"); // 創建a標簽 const href = window.URL.createObjectURL(res); // 創建下載的鏈接 aEle.href = href; aEle.download = "信用報告模型.xlsx"; // 下載后文件名 document.body.appendChild(aEle); aEle.click(); // 點擊下載 document.body.removeChild(aEle); // 下載完成移除元素 window.URL.revokeObjectURL(href); // 釋放掉blob對象 },
除了使用axios請求之外,還有a標簽也可以下載二進制數據流的文件,通過a標簽打開一個新的鏈接。下面是具體的實現:
<a :href="hrefValue">導出數據</a>
computed: { hrefValue() { return this.multipleSelection.length === 0 ? "" : "/api/demo/result/exportExcel?ids=" + this.multipleSelection.join(","); }, },
multipleSelection是選擇的數據的id集合,最后通過字符串的形式傳遞給后端。
a標簽這種方法比axios簡單許多。推薦使用這種方法。
這種寫法需要設置代理服務。不然請求不到
在vue.config.js中設置代理服務:
devServer: { port: port, open: true, overlay: { warnings: false, errors: true, }, proxy: { "/api": { target: "http://192.xxx.xx.xx:8000", }, "/profile": { target: "http://192.xxx.xx.xx:8000
", }, }, },
這樣請求路徑就變成了 http://192.xxx.xx.xx:8000/api/demo/result/exportExcel?ids=" +this.multipleSelection.join(",")