后端接口
提供两个接口,一个接口负责生成文件,一个接口负责下载本机文件。
1 生成文件需要前端传入参数,返回给前端生成的文件路径。
2 调用下载接口时,传入文件路径,将文件流返回给前端(放在response里面)。
控制层
/**
* 导出文件
* @param searchRequest
* @return
*/
@RequestMapping("/export")
@ResponseBody
@BussinessLog(value = "导出数据",module = "嗷呜")
public Object export(SearchRequest searchRequest){
String export = service.export(searchRequest);
return export;
}
/**
* 下载文件
* @param filepath
* @return
*/
@RequestMapping("/download")
@ResponseBody
@BussinessLog(value = "下载数据",module = "嗷呜")
public void downloadFile(HttpServletResponse response, @RequestParam("filepath") String filepath){
service.downloadFile(response,filepath);
}
service层
导出文件的方法
public String export(SearchRequest search) {
String type = search.getType();
Map<String, String> map = new HashMap<String, String>();
List<String> dataList = new ArrayList<String>();
// 生成文件的路径
String dowloadDataPath = dataLocalPath + "downloadData/" + type + "/";
// 生成文件夹/文件方法
FileUtil.CreateMultilayerFile(dowloadDataPath);
dowloadDataPath += type + DateUtil.getAllTime() + ".txt";
logger.info("dowloadData backPath=" + dowloadDataPath);
long searchBegin = System.currentTimeMillis();
// 查询数据库,将数据转成list
dataList = downloadData(search);
long searchTime = System.currentTimeMillis() - searchBegin;
logger.info("download查询数据" + dataList.size() + "条,耗时: " + (searchTime / 1000.0) + "'s!");
TxtUtils.writeTxt(dowloadDataPath, dataList);
return dowloadDataPath.replace( dataLocalPath + "downloadData/","");
}
/**
* 写入文件的方法
*/
public static void writeTxt(String txtPath, List<String> content) {
FileWriter fileWriter = null;
File file = new File(txtPath);
try {
if (file.exists()) {
//判断文件是否存在,如果不存在就新建一个txt
file.createNewFile();
}
fileWriter = new FileWriter(file);
logger.info("开始写入txt文件");
logger.info("content size=" + content.size());
for (String tar : content) {
fileWriter.write(tar);
}
fileWriter.flush();
fileWriter.close();
logger.info("写入txt文件结束");
} catch (Exception e) {
e.printStackTrace();
}
}
下载文件的方法。
public void downloadFile(HttpServletResponse response,String filePath) throws IOException {
File file = new File(filePath);
// 设置响应头信息
String filename = file.getName();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
// 指定下载文件名(attachment-以下载方式保存到本地,inline-在线预览)
response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
// 内容类型为通用类型,表示二进制数据流
response.setContentType("application/octet-stream");
try(InputStream is = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())){
byte[] buff = new byte[2048];
int len;
while ((len = bis.read(buff)) != -1){
bos.write(buff, 0, len);
}
bos.flush();
} catch (IOException e){
throw e;
}
}
前端
html部分
展示给用户的只有一个导出按钮,但实际上还有一个隐藏的a标签,是用来弹出下载框的。因为如果只有一个导出按钮,点击这个按钮请求ajax请求,将文件流返回,前端默认不会弹出下载框。ajax是异步请求,没有这个功能。
经过实际验证,点击a标签可以弹出下载文件的按钮。所以我在里面加了一个隐藏的a标签。导出任务完成后,通过js来模拟点击这个a标签,就可以弹出下载框了。
<div class="layui-inline">
<a id="downloadFile" hidden="hidden"><span id="downloadSpan">下载</span></a>
<button id="btnExport" class="layui-btn icon-btn" type="button">导出</button>
</div>
js部分
在导出方法的成功回调方法中,给a标签动态加上重定向的地址。也就是href地址,添加后,调用点击方法。
这里有一个重点:需要调用点击a标签中的span标签
原来我是直接调用的a标签点击,但是死活不出来下载框。我把隐藏属性取消后,手动点击是可以下载的,百思不得其解。查阅各大博文后,在中间加了一个span标签,通过点击这个span,会触发a标签的点击事件。
TemplateTable.export = function () {
let queryData = {};
queryData['timeLimit'] = $("#timeLimit").val();
queryData['charge'] = charge;
queryData['fuzzy'] = $('input[name="isFuzzySearch"]:checked').val();
let ajax = new $ax(Feng.ctxPath + "/export", function (data) {
$('#downloadFile').attr("href",contextPath + Feng.ctxPath + '/download?filepath='+data);
$('#downloadSpan').trigger("click");
Feng.success("成功导出!");
}, function (data) {
console.log("data:"+data);
Feng.error("导出失败!" + data.message + "!");
});
ajax.setData(queryData);
ajax.start();
};
大功告成