前端框架easyUI,后端java,使用poi导出数据暂存到服务器,然后打包下载。
用于解决大数据一次性导出到一个excel内存溢出问题和请求超时504问题
1,前端请求js
$$.openProcessingDialog(); //打开等待动画
$.ajax({
type: "POST",dataType: "JSON",
url:exportUrl,
data: {search_condition:JSON.stringify(params)},
success: function(data){
$$.closeProcessingDialog();//关闭等待动画
if(data.errorCode==0){
//执行下载
$(formSearchTemplate).form('submit', {
url : "/kywdop/actions/tool/download.do?actionId=tool_downloadFile",
onSubmit : function(param) {
param.FileName = data.msg;
}
})
}else{
alert(data.msg);
}
}
});
2,打包请求
与业务相关操作,数据是模拟的假数据,可以通过改变循环变量测试大数据的导出\
区分数据量导出不同格式数据,\
如果totnum<100w条,导出一个excel文件\
如果totnum>100w条,每10w条导出到一个excel,打成压缩包下载
@RequestMapping(value="/exportShiperrdtl")
public CIPResponseMsg testExport(CIPReqParameter parameter,HttpServletRequest request,HttpServletResponse response){
CIPResponseMsg res = new CIPResponseMsg();
//模拟数据总数
int totnum = 2200000;
//每个excel最大行数
int maxrow = 1000000;
long start = System.currentTimeMillis();
//页数,每页一个单独的excel文件
int page = totnum%maxrow==0?totnum/maxrow:totnum/maxrow+1;
/** 1.创建临时文件夹 */
//String rootPath = request.getSession().getServletContext().getRealPath("/");
String rootPath = "D:\\u02\\real_file\\kywdop\\";
if(page>1){
File temDir = new File(rootPath + UUID.randomUUID().toString().replaceAll("-", ""));
if(!temDir.exists()){
temDir.mkdirs();
}
String filepath = temDir.getPath();
/** 2.生成需要下载的文件,存放在临时文件夹内 */
List<File> srcfile = new ArrayList<File>();
//填充数据行
for(int i=1;i<=page;i++){
SXSSFWorkbook wb = new SXSSFWorkbook();
//新建sheet页
Sheet sheet = wb.createSheet();
//设置表头
Row row = sheet.createRow(0);
CellStyle style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
Cell cell = row.createCell((short) 0);
cell.setCellValue("姓名");
cell.setCellStyle(style);
cell = row.createCell((short) 1);
cell.setCellValue("单位");
cell.setCellStyle(style);
cell = row.createCell((short) 2);
cell.setCellValue("科室");
cell.setCellStyle(style);
cell = row.createCell((short) 3);
cell.setCellValue("注册日期");
cell.setCellStyle(style);
//模拟数据
for (int p = 1; p< maxrow; p++) {
row = sheet.createRow(p);
row.createCell((short) 0).setCellValue("大名"+p);
row.createCell((short) 1).setCellValue("单位"+p);
row.createCell((short) 2).setCellValue("科室"+p);
row.createCell((short) 3).setCellValue((new Date()).toString());
}
String fileName = "\\用户信息" + i+".xls";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filepath+fileName);
wb.write(fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
res.msg="服务器错误";
res.errorCode=1;
return res;
} catch (IOException e) {
e.printStackTrace();
res.msg="服务器错误";
res.errorCode=1;
return res;
}
srcfile.add(new File(filepath+fileName));
}
/** 3.调用工具类,生成zip压缩包 */
String zipfilepath = rootPath+"excel.zip"; //压缩包文件路径
try {
FileOutputStream fos2 = new FileOutputStream(new File(zipfilepath));
ZipUtils.toZip(srcfile, fos2);
} catch (IOException e) {
e.printStackTrace();
}
/** 4.删除临时文件和文件夹 */
File[] listFiles = temDir.listFiles();
for (int i = 0; i < listFiles.length; i++) {
listFiles[i].delete();
}
temDir.delete();
res.msg=zipfilepath;
}else{
SXSSFWorkbook wb = new SXSSFWorkbook();
//新建sheet页
Sheet sheet = wb.createSheet();
//设置表头
Row row = sheet.createRow(0);
CellStyle style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
Cell cell = row.createCell((short) 0);
cell.setCellValue("姓名");
cell.setCellStyle(style);
cell = row.createCell((short) 1);
cell.setCellValue("单位");
cell.setCellStyle(style);
cell = row.createCell((short) 2);
cell.setCellValue("科室");
cell.setCellStyle(style);
cell = row.createCell((short) 3);
cell.setCellValue("注册日期");
cell.setCellStyle(style);
//模拟数据
for (int p = 1; p< maxrow; p++) {
row = sheet.createRow(p);
row.createCell((short) 0).setCellValue("大名"+p);
row.createCell((short) 1).setCellValue("单位"+p);
row.createCell((short) 2).setCellValue("科室"+p);
row.createCell((short) 3).setCellValue((new Date()).toString());
}
String fileName = "用户信息.xls";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(rootPath+fileName);
wb.write(fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
res.msg="服务器错误";
res.errorCode=1;
return res;
} catch (IOException e) {
e.printStackTrace();
res.msg="服务器错误";
res.errorCode=1;
return res;
}
res.msg=rootPath+fileName;
}
long end = System.currentTimeMillis();
log.info("导出耗时:"+(end-start)+" ms");
res.errorCode=0;
return res;
}
百万数据每个excel导出多少条可以根据自己业务数据调试,每个excel导出越少,分的excel个数越多。
以上代码我替换为真实数据后的测试
性能测试
70w数据 每页10w –> 174977ms
70w数据 每页20w –> 154977ms
70w数据 每页30w –> 163430ms
70w数据 每页40w –> 141162ms
70w数据 每页50w –> 180974ms
3,下载通用controller
通用的文件下载只需要传入服务器的文件全路径即可下载,不区分文件是压缩包还是excel
/**
* 文件下载
* @param request
* @param response
* @throws IOException
*/
@RequestMapping(value="/download")
public void templateDownload(HttpServletRequest request, HttpServletResponse response) throws IOException{
String path = request.getParameter("FileName");
File file = new File(path);
String fileName = StringUtils.substringAfterLast(path,"\\");
if (request.getHeader("User-Agent").toUpperCase().indexOf("MSIE") > 0) { //IE浏览器
fileName = URLEncoder.encode(fileName, "UTF-8");
} else { //非IE浏览器
fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
}
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;fileName="+fileName);
//输入流写入输出流
InputStream inputStream = new FileInputStream(file);
OutputStream ouputStream = response.getOutputStream();
byte b[] = new byte[1024];
int n ;
//循环读取 !=-1读取完毕
while((n = inputStream.read(b)) != -1){
//写入到输出流 从0读取到n
ouputStream.write(b,0,n);
}
//关闭流、释放资源
ouputStream.close();
inputStream.close();
}