导出文件,能够导出但是文件打不开
背景:
在项目开发中,对于列表的查询,而后会有导出功能,这里导出的是一个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封装:
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 = true
const 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.data
if (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;
接口:
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;
导出封装的方法:
/**
*
* @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.data
const 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)
}
}
}
使用导出的方法:
//只是举例,根据实际进行调整
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',