通常前后端数据交互都是用JQuery的ajax函数,其返回类型只有xml、text、json、html等类型,没有“流”类型,所以我们无法使用ajax实现文件下载。下面介绍几种文件下载的思路。
一、windows.open下载文件
后端返回的是文件流
1.1 前端代码
var downloadURL = "appraise/download?flightNo=123";
window.open(downloadURL);
1.2 后端代码
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
//通知浏览器下载文件而不是打开
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xls");
//工作薄
HSSFWorkbook workbook = new HSSFWorkbook();
int index = 0;
for(Map.Entry> entry : evaluationTypeMap.entrySet()){
if(providers.containsKey(entry.getKey())){
AbstractEvaluationProvider evaluationDataProvider = providers.get(entry.getKey());
ExportExcelUtils.exportExcel(workbook,index++,evaluationDataProvider.createSheet(),evaluationDataProvider.createHead(),evaluationDataProvider.createData(entry.getValue()));
}
}
workbook.write(response.getOutputStream());
1.3 优点
- 浏览器兼容性好
1.4 缺点
- URL长度受限制
- 拿不到后端处理这个过程的时机,无法根据回调函数做交互以及进度提示
二、ajax提交请求,后端返回文件在线地址
后端返回的是文件地址(文件地址可访问)
2.1 前端代码
$.ajax({
type: "post",
url: "appraise/download",
data: {'flightNo':'123'},
success: function (res) {
if (res.Status) {
// window.open或者a标签下载
var isSupportDownload = 'download' in document.createElement('a');
if (isSupportDownload) {
var $a = $("");
$a.attr({
href: res.url,
download: 'filename'
}).hide().appendTo($("body"))[0].click();
} else {
window.open(res.url)
}
} else {
alert(res.Message);
}
}
})
2.2 后端代码
return "doc/adscf-1123-221ss-dda.doc";
2.3 优点
- 可以获取文件返回时机,可以做交互
2.4 缺点
- 线上产生大量的中间临时文件,可以用设置时限来优化或可使用大厂的云存储,从而减少临时文件的产生
三、使用form.submit下载文件
后端返回文件流
3.1 前端代码
try{
var exportForm = $("");
exportForm.append("");
exportForm.append("");
exportForm.append("");
$(document.body).append(exportForm);
exportForm.submit();
}catch (e) {
alert_prompt(e);
}finally {
exportForm.remove();
}
3.2 后端代码
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
//通知浏览器下载文件而不是打开
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xls");
//工作薄
HSSFWorkbook workbook = new HSSFWorkbook();
int index = 0;
for(Map.Entry> entry : evaluationTypeMap.entrySet()){
if(providers.containsKey(entry.getKey())){
AbstractEvaluationProvider evaluationDataProvider = providers.get(entry.getKey());
ExportExcelUtils.exportExcel(workbook,index++,evaluationDataProvider.createSheet(),evaluationDataProvider.createHead(),evaluationDataProvider.createData(entry.getValue()));
}
}
workbook.write(response.getOutputStream());
3.3 优点
- 兼容性良好,传统方式,不会出现URL长度限制问题
3.4 缺点
- 拿不到后端处理这个过程的时机,无法根据回调函数做交互以及进度提示
四、使用 jquery-download 插件
- jquery-download 下载地址:https://github.com/johnculviner/jquery.fileDownload/blob/master/src/Scripts/jquery.fileDownload.js
- jquery-download CDN地址:https://www.bootcdn.cn/jquery.fileDownload/
4.1 前端代码
$.fileDownload('appraise/downLoad.jhtml', {
httpMethod: 'post',
data: {'flightLeftDate': flightLeftDate,'flightRightDate':flightRightDate},
prepareCallback: function (url) {
console.log("文件下载中...");
// 数据加载动画
$("#loading").modal('show');
},
abortCallback: function (url) {
// 异常终止
alert_prompt("文件下载异常!");
$("#loading").modal('hide');
},
successCallback: function (url) {
alert_prompt("文件下载成功!");
$("#loading").modal('hide');
},
failCallback: function (html, url) {
if(html.indexOf('<') >= 0) {
html = $(html).text();
}
var result = JSON.parse(html);
$("#loading").modal('hide');
alert_prompt("文件下载失败:" + result.Head.Msg);
}
});
4.2 后端代码
if(!evaluationTypeMap.isEmpty()){
try{
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xls");
response.setHeader("Set-Cookie", "fileDownload=true; path=/");
//工作薄
HSSFWorkbook workbook = new HSSFWorkbook();
int index = 0;
for(Map.Entry> entry : evaluationTypeMap.entrySet()){
if(providers.containsKey(entry.getKey())){
AbstractEvaluationProvider evaluationDataProvider = providers.get(entry.getKey());
ExportExcelUtils.exportExcel(workbook,index++,evaluationDataProvider.createSheet(),evaluationDataProvider.createHead(),evaluationDataProvider.createData(entry.getValue()));
}
}
workbook.write(response.getOutputStream());
}catch (Exception e){
log.error("efb:评价导出---->导出失败:",e);
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
EfbReturnPO efbReturnPO = new EfbReturnPO();
efbReturnPO.setUnSuccessHead("下载文件失败:" + e.getMessage());
response.setHeader("Set-Cookie", "fileDownload=false; path=/");
response.getWriter().println(AOSJson.toJson(efbReturnPO));
}
}else{
log.error("efb:评价导出---->导出失败:无匹配数据");
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
EfbReturnPO efbReturnPO = new EfbReturnPO();
efbReturnPO.setUnSuccessHead("无匹配数据!");
response.setHeader("Set-Cookie", "fileDownload=false; path=/");
response.getWriter().println(AOSJson.toJson(efbReturnPO));
}
注意:后端代码增加了一个名为"fileDownload"的cookie的返回;jquery.download.js插件使用该cookie来判断是否下载成功,从而进入成功回调函数(successCallback)
4.3 优点
- 浏览器兼容好,此插件做了多种兼容
- window.open(url)打开某个文件地址
- iframe的框架中,设置src属性,通过iframe进行文件的下载,支持文件地址
- 通过form标签,设置action的文件地址,然后通过form的提交来完成文件的下载
- 可以获取文件返回时机,可以做交互
五、其它方案
- iframe直接向后端提交,实现对文件流进行下载
- Html5的Blob对象实现对文件流进行下载
- file-saver实现对文件流进行下载