业务需要导出表格数据,后端返回的是二进制数据流,使用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(",")