目录
1.下载方法封装
①说明
前端的请求大概分为三种类型
普通请求:常用的get,post,put,delete等请求
上传请求:使用post请求,发送formdata对象的参数,formdata中存放文件及其他参数
下载请求:使用post请求,设置响应格式为blob或者arraybuffer
②通用下载请求说明
api:
// 共通下载方法
export function dowload(url:string,params:any,filename:string,config:any) {
return axios.post(url,params,{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config})
.then((data:any) =>{
if(data.type !== 'application/json'){
const blob = new Blob([data],{ type: 'application/vnd.ms-excel' })
saveAs(blob,filename)
// let objectUrl = URL.createObjectURL(blob) // 创建URL
// let link = document.createElement("a");
// link.href = objectUrl
// link.download =filename // 自定义文件名
// link.click() // 下载文件
// URL.revokeObjectURL(objectUrl); // 释放内存
}
}).catch((r:any) =>{
console.log(r)
Message.error({ content: '下载文件出现错误,请联系管理员!', position: 'top' });
})
}
说明:
前端下载后端的文件,一般分为两种类型,后端返回文件流或者后端将文件存储在服务器,将地址返回至前端。
我使用的是后端返回文件流的方式,前端的请求需要将responseType设置为blob格式,代表后端返回的是二进制文件流。content-type按照需要进行设置,可以设置为application/x-www-form-urlencoded或者application/json,不同的方式决定了后端接收参数的方式不一样。
拦截器设置:
/**
* 请求拦截
*/
axios.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = getToken();
if (token) {
if (!config.headers) {
config.headers = {};
}
config.headers["tianjiannongshi_token"] = token;
config.headers["X-Requested-With"] = "ajax";
}
if (!config.url?.startsWith("/em/file")) {
config.data = {
reqBody: config.data,
};
}
config.url = import.meta.env.VITE_APP_API_URL + config.url;
return config;
},
(error) =>
// do something
Promise.reject(error)
);
/**
* 响应拦截
*/
axios.interceptors.response.use(
(response: AxiosResponse<any>) => {
// 二进制数据则直接返回
if (response.request.responseType === 'blob') {
return response.data
}
const res = response.data;
let resCode = res.resHdr?.resCode;
let resMsg = res.resHdr?.resMsg || "请求未知异常";
if (resCode == "9990_10" || resCode == "9990_9") {
Message.error({
content: "登录信息过期,请重新登录",
duration: 5 * 1000,
});
// 跳转登录页面
router.push("/login");
return Promise.reject(new Error(resMsg));
}
if (resCode !== "0000") {
Message.error({
content: resMsg,
duration: 5 * 1000,
});
return Promise.reject(new Error(resMsg));
}
return res.resBody;
},
(error) => {
Message.error({
content: error.msg || "请求未知异常",
duration: 5 * 1000,
});
return Promise.reject(error);
}
);
说明:
响应拦截器中要对responseType进行判断,如果是blob格式的,直接返回response中的data,其他格式时,一般为json,需要获取返回的data的返回值进行判断,登录过期时跳转到登录画面,并进行信息提示,异常时进行信息提示,正常时返回data,然后在接口中对data的内容进行处理。
2.将后端返回的文件流转换为文件
在通过下载方法中,获取到返回的data数据,需要将数据流转为文件。
方式1:
// 共通下载方法
export function dowload(url:string,params:any,filename:string,config:any) {
return axios.post(url,params,{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config})
.then((data:any) =>{
if(data.type !== 'application/json'){
const blob = new Blob([data],{ type: 'application/vnd.ms-excel' })
let objectUrl = URL.createObjectURL(blob) // 创建URL
let link = document.createElement("a");
link.href = objectUrl
link.download =filename // 自定义文件名
link.click() // 下载文件
URL.revokeObjectURL(objectUrl); // 释放内存
}
}).catch((r:any) =>{
console.log(r)
Message.error({ content: '下载文件出现错误,请联系管理员!', position: 'top' });
})
}
将data转换为blob对象,然后根据blob创建url,再创建一个a元素,将a元素的href属性和url进行绑定,再设置下载的文件名,触发click事件进行下载。
方式2:
使用file-saver方式
①安装依赖:
pnpm install file-saver --save
pnpm install @types/file-saver --save-dev
②使用
import { saveAs } from 'file-saver'
// 共通下载方法
export function dowload(url:string,params:any,filename:string,config:any) {
return axios.post(url,params,{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config})
.then((data:any) =>{
if(data.type !== 'application/json'){
const blob = new Blob([data],{ type: 'application/vnd.ms-excel' })
saveAs(blob,filename)
}
}).catch((r:any) =>{
console.log(r)
Message.error({ content: '下载文件出现错误,请联系管理员!', position: 'top' });
})
}
调用saveAs方式,传递blob对象及文件名即可
3.下载时传递参数
①在画面中调用方法
await dowload('/book/file/mb1003', {reqBody: {errList: res.errList}}, `错误信息_${moment().format('yyyyMMDDHHmmss')}.xlsx`, {})
②通用下载方法的封装
设置请求格式为application/json
import axios from "axios";
import { Message } from '@arco-design/web-vue';
import { saveAs } from 'file-saver'
// 共通下载方法
export function dowload(url:string,params:any,filename:string,config:any) {
return axios.post(url,params,{ headers: { 'Content-Type': 'application/json' },
responseType: 'blob',
...config})
.then((data:any) =>{
if(data.type !== 'application/json'){
const blob = new Blob([data],{ type: 'application/vnd.ms-excel' })
saveAs(blob,filename)
// let objectUrl = URL.createObjectURL(blob) // 创建URL
// let link = document.createElement("a");
// link.href = objectUrl
// link.download =filename // 自定义文件名
// link.click() // 下载文件
// URL.revokeObjectURL(objectUrl); // 释放内存
}
}).catch((r:any) =>{
console.log(r)
Message.error({ content: '下载文件出现错误,请联系管理员!', position: 'top' });
})
}
③后端接收参数,并进行处理
@RequestMapping(value = "/xxx", method = {RequestMethod.POST})
public void xxx(@RequestBody InventoryDowloadDomain domain, HttpServletResponse response) throws Exception {
InventoryDowloadDomain.RequestBody reqBody = domain.getReqBody();
InventoryDowloadSpecDomain dowDomain = new InventoryDowloadSpecDomain();
List<String> colId = reqBody.getColId();
colId.add(ColumnIDEnum.INVRECID.type);
dowDomain.setColId(colId);
String fileName = "导入模板" + DateFormatUtils.format(new Date(), CommonContants.DATE_TYPE) + CommonContants.TYPE_XLSX;
response.setContentType(CommonContants.CONTENT_TYPE);
response.setCharacterEncoding(CommonContants.UTF8);
response.setHeader(Header.CONTENT_DISPOSITION.getValue(), CommonContants.CONTENT_TYPE + URLEncoder.encode(fileName, CommonContants.UTF8));
DowloadDomain dowloadDomain = attributeCustomizeService.dowloadTemplate(dowDomain);
List<List<Object>> total = new ArrayList<>();
EasyExcel.write(response.getOutputStream()).head(dowloadDomain.getHeads())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new CustomSheetWriteHandler(dowloadDomain.getValList()))
.sheet("模板").doWrite(total);
}
4.下载时后端处理异常时,将后端的异常信息进行显示
①前端页面中调用
const exportBook = async () => {
let colIdList = colData.value.map((item: { colId: string; }) => item.colId)
let colInfo = newShowColIds.value.length == 0 ? colIdList : newShowColIds.value
const req = {
docId: docId.value,
colIdList: colInfo,
sort: sortRule.value,
filter: filterRule.value,
filterLogic: filterLogic.value
}
await dowload('/book/file/mb1006', {reqBody: req}, `book_${moment().format('yyyyMMDDHHmmss')}.xlsx`, {})
}
②拦截器配置不变
③共同下载封装
追加后端返回数据的类型时application/json的处理,将后端的data转换为json对象,将对象中的error信息进行提示
// 共通下载方法
export function dowload(url: string, params: any, filename: string, config: any) {
return axios.post(url, params, {
headers: {'Content-Type': 'application/json'},
responseType: 'blob',
...config
})
.then(async (data: any) => {
if (data.type !== 'application/json') {
const blob = new Blob([data], {type: 'application/vnd.ms-excel'})
saveAs(blob, filename)
// let objectUrl = URL.createObjectURL(blob) // 创建URL
// let link = document.createElement("a");
// link.href = objectUrl
// link.download =filename // 自定义文件名
// link.click() // 下载文件
// URL.revokeObjectURL(objectUrl); // 释放内存
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
console.log(rspObj)
Message.error({content: rspObj?.resHdr?.resMsg, position: 'top'});
}
}).catch((r: any) => {
console.log(r)
Message.error({content: '下载文件出现错误,请联系管理员!', position: 'top'});
})
}
④后端处理
当service返回值为失败时,重置response,并将异常信息json化后返回。
public void mb1001(@RequestBody InventoryDowloadDomain domain, HttpServletResponse response) throws Exception {
InventoryDowloadDomain.RequestBody reqBody = domain.getReqBody();
InventoryDowloadSpecDomain dowDomain = new InventoryDowloadSpecDomain();
dowDomain.setColId(reqBody.getColId());
String fileName = "导入模板" + DateFormatUtils.format(new Date(), CommonContants.DATE_TYPE) + CommonContants.TYPE_XLSX;
response.setContentType(CommonContants.CONTENT_TYPE);
response.setCharacterEncoding(CommonContants.UTF8);
response.setHeader(Header.CONTENT_DISPOSITION.getValue(), CommonContants.CONTENT_TYPE + URLEncoder.encode(fileName, CommonContants.UTF8));
RtnBean rtnBean = attributeCustomizeService.dowloadTemplate(dowDomain);
DowloadDomain dowloadDomain = (DowloadDomain) rtnBean.getRtnObj();
if (!rtnBean.getRsl()) {
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
ResponseBean responseBean = new ResponseBean("mb1001", rtnBean.getResMsg().getRetCode(), rtnBean.getMessage(), rtnBean.getRtnObj());
response.getWriter().println(JSON.toJSONString(responseBean));
} else {
List<List<Object>> total = new ArrayList<>();
EasyExcel.write(response.getOutputStream()).head(dowloadDomain.getHeads())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new CustomSheetWriteHandler(dowloadDomain.getValList()))
.sheet("模板").doWrite(total);
}
}
5.总结
注意通过下载方法的封装
注意响应拦截器首先要对响应类型进行判断,blob格式直接返回data
生成文件直接使用file-saver方式,比较方便