解决Ajax/Axios请求下载无效的问题
起因
公司用vue搭了一个前端,现在需要一个文件导出下载的功能,向后台发请求传参下载,too young的我当然是想当然的用了axios发了post请求,结果发现页面死活没有反应。因为后台也是我写的,为了确认不是后台代码的毛病,我把拦截关了用location.href试了试,发现可行,文件下载成功。那么问题就出在axios请求上了。
原因
众所周知,Ajax/Axios请求实际上是通过XMLHttpRequest实现的,具体请自行百度。
request请求只是个“字符型”的请求,即请求的内容是以文本类型存放的。文件的下载是以二进制形式进行,虽然可以读取到返回的response,但只是读取,无法执行。也就是说前端无法调用到浏览器的下载处理机制和程序。
解决方法
通过blob(用来存储二进制大文件)包装ajax(或axios)请求到的data数据,实现下载EXCEL(或其他如图片等)文件。
//案例一
axios:设置返回数据格式为blob或者arraybuffer
如:
var instance = axios.create({ ... //一些配置
responseType: 'blob', //返回数据的格式,可选值为arraybuffer,blob,document,json,text,stream,默认值为json
})
请求时的处理:
getExcel().then(res => {
//这里res.data是返回的blob对象
var blob = new Blob([res.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'}); //application/vnd.openxmlformats-officedocument.spreadsheetml.sheet这里表示xlsx类型
var downloadElement = document.createElement('a');
var href = window.URL.createObjectURL(blob); //创建下载的链接
downloadElement.href = href;
downloadElement.download = 'xxx.xlsx'; //下载后文件名
document.body.appendChild(downloadElement);
downloadElement.click(); //点击下载
document.body.removeChild(downloadElement); //下载完成移除元素
window.URL.revokeObjectURL(href); //释放掉blob对象
})
//案例二
function createDownload(fileName, content){
var blob = new Blob([content]);
var link = document.createElement("a");
link.innerHTML = fileName;
link.download = fileName;
link.href = URL.createObjectURL(blob);
document.getElementsByTagName("body")[0].appendChild(link);
}
createDownload("download.txt","download file");
//案例三<br>
function downloadExport(data) {
return axios.post(url, data).then((res)=>{
const content = res
const blob = new Blob(["\uFEFF" + content.data],{ type: "application/vnd.ms-excel;charset=utf-8"})
const fileName = '卡密.xls'
if ('download' in document.createElement('a')) { // 非IE下载
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // 释放URL 对象
document.body.removeChild(elink)
} else { // IE10+下载
navigator.msSaveBlob(blob, fileName)
}
});
}
下面是我的代码,前端部分:
//发送请求
exportExcel(param).then(res => {
const blob = new Blob([res]);
const fileName = '导出信息.xls';
if ('download' in document.createElement('a')) { // 非IE下载
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);// 释放URL 对象
document.body.removeChild(elink);
} else { // IE10+下载
navigator.msSaveBlob(blob, fileName);
}
});
这里在之后的测试中还存在一个问题,用Edge浏览器(没错就是巨硬家的那个)无法下载,在控制台看了一下,是因为response里什么都没有。考虑到Edge浏览器实在是…所以暂时没管它,加了个“使用Edge浏览器无法下载”的提示,之后有必要再改。至于Chrome,FireFox,360,IE等都OK。
后端部分:
@RequestMapping(value = "/exportExcelFile",method = {RequestMethod.GET,RequestMethod.POST})
public ResponseEntity<byte[]> export(String deviceCode, String batchNo, @RequestParam(required = false, value = "streetIds") List<String> streetIds, String type) throws IOException {
List<Object> models = getExportModelList(deviceCode,batchNo,streetIds,type);
File file = getExportExcelFile(models,type);
System.out.println(file.getName());
byte[] fileBytes = FileUtils.readFileToByteArray(file);
file.delete();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "export.xls");
return new ResponseEntity<>(fileBytes, headers, HttpStatus.CREATED);
}
getExportModelList
:获取数据getExportExcelFile
:将数据通过OutPutExcel类返回一个file(OutPutExcel是自写类,详情自行百度利用freemaker导出文件)