背景:
在项目开发中,对于列表的查询,而后会有导出功能,这里导出的是一个excell表格。实现了两种,1.导出的文件,命名是前端传输过去的;2.导出的文件,命名是根据后端返回的文件名获取的。
//解码获取导出文件名
const fileNames = res.headers['content-disposition']
const fileName = decodeURIComponent(fileNames.match(/=(.*)$/)[1])
//能够导出但是打不开,导出接口的网络请求要带上responseType: 'blob',
//导出文件【get】
exportCrewInfoFile(params) {
return request.Get("/data/ferryShip/download?", params, {
headers: {
"Content-Type": "application/json",
},
responseType: 'blob',
});
},
//导出文件【post】
exportWarningFile(params) {
return request.Post("/data/warningRecord/download", params, {
headers: {
"Content-Type": "application/json",
},
responseType: 'blob',
});
},
前端界面:
前端请求接口,axios发起网络请求:
axios封装:
javascript">import axios from "axios";
import useShoreBasedStore from "@/store";
import qs from 'qs';
import router from "@/router/index.js";
import { ElMessageBox } from "element-plus";let messageBoxVisible = false
export const BASEUrl = import.meta.env.VITE_USER_NODE_ENV === 'development' ? '/apiproxy/pa' : import.meta.env.VITE_API_URL + '/pa'
const request = axios.create({baseURL: BASEUrl,timeout: 3000 * 60,headers: { "Content-Type": "application/x-www-form-urlencoded" },
});/*** 请求拦截添加token* */
request.interceptors.request.use((config) => {const user = useShoreBasedStore();if (user.userInfo.token) {config.headers.Authorization = user.userInfo.token;}return config;
}, (error) => {return Promise.reject(error)
});const showMsg = () => {if (!messageBoxVisible) {messageBoxVisible = trueconst user = useShoreBasedStore()ElMessageBox.confirm('您的登录信息已过期,请重新登录!','温馨提示',{confirmButtonText: '确认',cancelButtonText: '取消',type: 'warning'}).then(() => {user.loginOut()router.push({ name: 'login' })}).catch(() => {}).finally(() => {messageBoxVisible = false})}
}request.interceptors.response.use((response) => {const data = response.dataif (data.status === 401 || data.code === 401) {showMsg()} else {return response}
}, (error) => {if (error.response.data.code === 401) {showMsg()} else {return Promise.reject(error)}
})// 定义get方法
request.Get = function (url, params, config) {if (params) {return request.get(url + qs.stringify(params), config);}return request.get(url, config);
};// 定义post方式
request.Post = function (url, params, config) {if (params) {return request.post(url, params, config);}return request.post(url, config);
};export default request;
接口:
javascript">import request from "../utils/request.js";const fileApi = {// 上传文件uploadFile(params) {return request.Post("/data/file/upload", params, {headers: {"Content-Type": "multipart/form-data",},});},// 删除文件deleteFile(params) {return request.Post("/data/file/delete", params, {headers: {"Content-Type": "application/json",},});},//导出文件【get】exportFerryPortFile(params) {return request.Get("/data/ferryPort/download?", params, {headers: {"Content-Type": "application/json",},responseType: 'blob',});},//导出文件【post】exportWarningFile(params) {return request.Post("/data/warningRecord/download", params, {headers: {"Content-Type": "application/json",},responseType: 'blob',});},
}
export default fileApi;
导出封装的方法:
javascript">/**** @param {*} fileContent 文件本体* @param {*} _fileName 自定义文件名*/
export const exportFileUtil = (fileContent, _fileName) => {const content = fileContent;const blob = new Blob([content], {type: fileContent.type || "application/octet-stream; charset=utf-8",});const fileName = _fileName;if ("download" in document.createElement("a")) {//非IE下载const a = document.createElement("a"); //创建一个a标签a.download = fileName; //指定文件名称a.style.display = "none"; //页面隐藏a.href = URL.createObjectURL(blob); // href用于下载地址document.body.appendChild(a); //插到页面上a.click(); //通过点击触发URL.revokeObjectURL(a.href); //释放URL 对象document.body.removeChild(a); //删掉a标签} else {//IE10 + 下载navigator.msSaveBlob(blob, fileName);}
};
/*** * @param {*} res 接口返回的文件流*/
export const dowloadFileUrl = (res) => {const fileNames = res.headers['content-disposition']if (fileNames) {//解码const fileName = decodeURIComponent(fileNames.match(/=(.*)$/)[1])// 处理返回的文件流const content = res.dataconst blob = new Blob([content], {type: res.data.type||"application/vnd.ms-excel"});if ('download' in document.createElement('a')) {//非IE下载const a = document.createElement('a') //创建一个a标签a.download = fileName //指定文件名称a.style.display = 'none' //页面隐藏a.href = URL.createObjectURL(blob) // href用于下载地址document.body.appendChild(a) //插到页面上a.click() //通过点击触发URL.revokeObjectURL(a.href) //释放URL 对象document.body.removeChild(a) //删掉a标签} else {//IE10 + 下载navigator.msSaveBlob(blob, fileName)}}
}
使用导出的方法:
javascript">//只是举例,根据实际进行调整
const exportExcel = () => {const search = searchForm.value.submitData();const params = {...search,};api.fileApi.exportCrewInfoFile(params).then((res) => {if (res.status === 200) {exportFileUtil(res.data, "渡口管理导出文件.xlsx");dowloadFileUrl(res)}}).finally((err) => {console.log(err);});
};
写到这儿,就实现了导出功能。。。下面的是导出接口的详细解释:
一、导出文件使用get请求
(1)、导出文件,get请求里面传参有数组等复杂数据结构
前端界面:
上图可以看见post查询接口可以返回4条数据,那么导出功能以同样的参数也应该导出4条数据的excell。
导出传参:
结果:
导出文件但是打不开,原因可能是:传参的问题;接口返回的数据有问题。
能够导出,不能打开文件。
经过排查,是前端请求有问题,如下图:
能够导出,并且能够打开的。get请求的响应体和请求体的结构。如下:请求传参带上,responseType: 'blob',【必须带上】
二、导出文件使用post请求
前端界面:
导出传参:
总结:
导出功能,不管前端使用的是get或者post请求,都需要后端对接受到的传参进行识别,进而返回对应的响应体。
前端请求一定要带上responseType: 'blob',