上次,我们说道了如何使用HSSFWorkbook(POI)实现导出excel的功能。而且我们也已经实现了。但是,我们发现,当我们导出几万设置几十万的数据时,就会很慢,而且还会造成内存溢出的问题。如果经常下载大量数据,还会占用服务器上的存储空间。今天,我们来优化一下我们的导出excel。
1、生成多个excel文件
当数据量比较大时,我们选择让每个excel分为多个sheet,并且生成多个excel,然后将多个excel压缩,最后输出。
/**
* 导出Excel
* @param excelName 要导出的excel名称
* @param list 要导出的数据集合
* @param fieldMap 中英文字段对应Map,即要导出的excel表头
* @param response 使用response可以导出到浏览器
* @return
*/
public static <T> void export(String excelName,List<T> list,LinkedHashMap<String, String> fieldMap,HttpServletResponse response){
// 设置默认文件名为当前时间:年月日时分秒
if (excelName==null || excelName=="") {
excelName = new SimpleDateFormat("yyyyMMddhhmmss").format(
new Date()).toString();
}
int total=list.size(); //总记录数
int excelBig=10000; //excel划分的大小
//判断需要分成多少个excel
int excelCount=(total%excelBig==0)?(total/excelBig):(total/excelBig+1);
//定义一个excel集合
List<HSSFWorkbook> excelList=new ArrayList<HSSFWorkbook>();
HSSFWorkbook wb=null; //excel工作表
int begin = 0; //数据源的开始位置
int end = 0; //数据源的结束位置
//循环得到excel集合
for(int i=0;i<excelCount;i++){
//第一次begin从零开始,否则从结束位置加1开始
if(end!=0){
begin=end;
}
end=begin+excelBig;
//判断如果最后的位置大于总记录数,则修改为总记录数
if(end>list.size()){
end=list.size();
}
//生成excel
wb=createExcel(excelName+(i+1), list,begin,end, fieldMap, response);
//将生成的excel添加到集合
excelList.add(wb);
}
//生成压缩文件,并导出
toZipOut(excelName, excelList, response);
}
2、每个excel生成多个sheet
/**
* 生成Excel
* @param excelName 要导出的excel名称
* @param list 要导出的数据集合
* @param begin 数据集合的开始位置
* @param end 数据集合的结束位置
* @param fieldMap 中英文字段对应Map,即要导出的excel表头
* @param response 使用response可以导出到浏览器
* @return
*/
private static <T> HSSFWorkbook createExcel(String excelName,List<T> list,Integer begin,Integer end,
LinkedHashMap<String, String> fieldMap,HttpServletResponse response){
int sheetContent=2000; //每个excel中sheet的容量
//计算sheet的数量
int sheetCount=0;
//如果总记录数 正好整除 以每个sheet的容量
if ((end-begin)%sheetContent==0){
//sheet的数量=总记录数 除以 每个sheet的容量
sheetCount=(end-begin)/sheetContent;
}else{
//sheet的数量=总记录数 除以 每个sheet的容量 加1
sheetCount=(end-begin)/sheetContent+1;
}
//创建一个WorkBook,对应一个Excel文件
HSSFWorkbook wb=new HSSFWorkbook();
//创建单元格,并设置值表头 设置表头居中
HSSFCellStyle style=wb.createCellStyle();
//创建一个居中格式
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
//定义sheet
HSSFSheet sheet=null;
//定义sheet的名称
String sheetName=excelName+"-";
//sheet中的数据开始位置
int beginSheet=0;
//sheet中的数据结束位置
int endSheet=0;
//循环创建sheet
for (int i=0;i<sheetCount;i++){
//在Workbook中,创建一个sheet,对应Excel中的工作薄(sheet)
sheet=wb.createSheet(sheetName+(i+1));
//第一次beginSheet从begin开始,否则从结束位置加1开始
if(endSheet==0){
beginSheet=begin;
}else{
beginSheet=endSheet;
}
//sheet结束位置=sheet开始位置+每个sheet的容量
endSheet=beginSheet+sheetContent;
//判断如果最后的位置大于总记录数,则修改为总记录数
if(endSheet>end){
endSheet=end;
}
// 填充工作表
try {
fillSheet(sheet,list,beginSheet,endSheet,fieldMap,style);
} catch (Exception e) {
logger.info(e.getMessage());
}
}
//返回excel
return wb;
}
3、将文件压缩为zip格式
/**
* 生成压缩文件,并导出
* @param excelName 要导出的excel名称
* @param excelList 要压缩的文件集合
* @param response 使用response可以导出到浏览器
* @return
*/
private static <T> void toZipOut(String excelName,List<HSSFWorkbook> excelList,HttpServletResponse response){
try {
//将压缩包的名字默认为日期时间串
String fieldName=new SimpleDateFormat("yyyyMMddhhmmss").format(
new Date()).toString();
/**设置response头信息**/
//重置response头信息
response.reset();
//自己写状态码
response.setStatus(HttpServletResponse.SC_OK);
//使客户端浏览器,区分不同种类的数据,并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。
//ZIP与EXE文件的MIME类型同为application/octet-stream
response.setContentType( "application/octet-stream ");
//Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
//Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
//此处设置下载文件的格式为.zip
response.setHeader("Content-Disposition","attachment;filename=\""+fieldName+".zip"+"\"");
//生成输出流
OutputStream ouputStream = response.getOutputStream();
//生成压缩包
ZipOutputStream zip = new ZipOutputStream(ouputStream);
zip.setEncoding("GBK");//指定编码为gbk,否则部署到linux下会出现乱码
int i = 1;
String eName;
//循环文件列表,添加到压缩包中
for(HSSFWorkbook workbook:excelList){
//给每个文件名加序号
eName=excelName+String.valueOf(i);
//实例化一个压缩实体
ZipEntry entry = new ZipEntry(eName+".xls");
//将压缩实体放入压缩包
zip.putNextEntry(entry);
//将excel内容写进压缩实体
workbook.write(zip);
i++;
}
//将文件输出
zip.flush();
zip.close();
response.flushBuffer();
} catch (Exception e) {
logger.info("导出Excel失败!");
logger.error(e.getMessage());
}
}
总结:
这里我们只是将大数据进行了切割,转而变成处理小数据,这只是做了一部分的优化处理,希望大家能有更好的优化方法。