在java项目中,使用xml配置和反射写了一个通用POI导出Excel数据的方法,发现导出2500行40列数据用了大概30分钟,通过排查发现POI的createCell非常耗损性能。然后采用JXL重构通用Excel数据导出方法,时间只需1s左右。现在把POI和JXL两种导出方法对比如下,也方便以后用到,便于检索。
POI方式写Excel
/**
* POI方式数据导出 支持duo sheet导出,支持表名、sheet页、标题、副标题单独命名
* 该方法由于性能问题,已过期,不推荐使用
*/
@Deprecated
@SuppressWarnings({ "unchecked", "deprecation" })
@RequestMapping(value = "/exportSheetsData")
public void exportSheetsData(String reqObjs, String tableName, HttpServletRequest request,
HttpServletResponse response) throws Exception {
long s=(new Date()).getTime();
long s1=0;
List queryConditions = JSON.parseArray(reqObjs, QueryCondition.class);
int conCount = queryConditions.size();
OutputStream fOut = null;
// 产生工作簿对象
HSSFWorkbook workbook = new HSSFWorkbook();
// 定义标题样式
HSSFFont titleFont = workbook.createFont();
titleFont.setFontHeightInPoints((short) 18);
titleFont.setFontName("宋体");
titleFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
HSSFCellStyle title = workbook.createCellStyle();
title.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
title.setFont(titleFont);
title.setAlignment(HSSFCellStyle.ALIGN_CENTER);
title.setAlignment(HSSFCellStyle.ALIGN_CENTER);
title.setWrapText(true);
// 定义表头样式
HSSFFont font = workbook.createFont();
font.setFontHeightInPoints((short) 12);
font.setFontName("宋体");
HSSFCellStyle head = workbook.createCellStyle();
head.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
head.setFont(font);
head.setAlignment(HSSFCellStyle.ALIGN_CENTER);
head.setBorderBottom((short) 1); // 设置边框样式
head.setBorderLeft((short) 1); // 左边框
head.setBorderRight((short) 1); // 右边框
head.setBorderTop((short) 1); // 顶边框
// 定义居中样式
HSSFCellStyle center = workbook.createCellStyle();
center.setFont(font);
center.setAlignment(HSSFCellStyle.ALIGN_CENTER);
center.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
center.setBorderBottom((short) 1); // 设置边框样式
center.setBorderLeft((short) 1); // 左边框
center.setBorderRight((short) 1); // 右边框
center.setBorderTop((short) 1); // 顶边框
center.setWrapText(true);
// 需要自动换行的样式
HSSFCellStyle huanHang = workbook.createCellStyle();
huanHang.setFont(font);
huanHang.setAlignment(HSSFCellStyle.ALIGN_LEFT);
huanHang.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
huanHang.setBorderBottom((short) 1); // 设置边框样式
huanHang.setBorderLeft((short) 1); // 左边框
huanHang.setBorderRight((short) 1); // 右边框
huanHang.setBorderTop((short) 1); // 顶边框
huanHang.setWrapText(true);
// 定义小数数字样式
HSSFCellStyle number = workbook.createCellStyle();
number.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00"));// 小数保留两位
number.setFont(font);
number.setAlignment(HSSFCellStyle.ALIGN_CENTER);
number.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
number.setBorderBottom((short) 1); // 设置边框样式
number.setBorderLeft((short) 1); // 左边框
number.setBorderRight((short) 1); // 右边框
number.setBorderTop((short) 1); // 顶边框
// 定义整数数字样式
HSSFCellStyle intNumber = workbook.createCellStyle();
intNumber.setDataFormat(HSSFDataFormat.getBuiltinFormat("0"));// 小数保留两位
intNumber.setFont(font);
intNumber.setAlignment(HSSFCellStyle.ALIGN_CENTER);
intNumber.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
intNumber.setBorderBottom((short) 1); // 设置边框样式
intNumber.setBorderLeft((short) 1); // 左边框
intNumber.setBorderRight((short) 1); // 右边框
intNumber.setBorderTop((short) 1); // 顶边框
// 定义居左样式
HSSFCellStyle left = workbook.createCellStyle();
left.setFont(font);
left.setAlignment(HSSFCellStyle.ALIGN_LEFT);
left.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
left.setWrapText(true);
try {
for (int qindex = 0; qindex < conCount; qindex++) {
/*--------------------------------------------
该段代码为基于xml与反射的数据获取过程 已经略去
-------------------------------------------------*/
s1=(new Date()).getTime();
// 产生工作表对象
HSSFSheet sheet = workbook.createSheet();
workbook.setSheetName(qindex, queryCondition.getSheetName() == null ? tableName : queryCondition
.getSheetName());
int rowLen = objList.size();
List colList = query.getColumnList();
int colLen = colList.size();
// 创建表头
int hempty = 0;
// 填充标题
HSSFRow hrow = sheet.createRow(0);
HSSFCell cell = hrow.createCell(0);
hrow.setHeight((short) 1500);
cell.setCellValue(new HSSFRichTextString(queryCondition.getSheetTitle() != null ? queryCondition.getSheetTitle()
: queryCondition.getSheetName()));
cell.setCellStyle(title);
int lastcol = -1;
for (int h = 0; h < colLen; ++h) {
Column hcolumn = colList.get(h);
if (!hcolumn.getIsServerCondition() && !hcolumn.getHidden() && hcolumn.getIsExport()) {
lastcol++;
}
}
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, lastcol));
int titleRowCount=1;
//填充副标题
hrow = sheet.createRow(titleRowCount);
if(queryCondition.getSheetSubTitle()!=null){
titleRowCount=2;
cell = hrow.createCell(0);
hrow.setHeight((short) 600);
cell.setCellValue(new HSSFRichTextString(queryCondition.getSheetSubTitle()));
cell.setCellStyle(left);
sheet.addMergedRegion(new CellRangeAddress(1,1,0,lastcol));
hrow=sheet.createRow(titleRowCount);
}
hrow.setHeight((short) 600);
for (int h = 0; h < colLen; ++h) {
Column hcolumn = colList.get(h);
if ("rowIndex".equals(hcolumn.getKey())) {
// 创建序号列
HSSFCell hcell = hrow.createCell(0);
hcell.setCellType(HSSFCell.CELL_TYPE_STRING);
hcell.setCellStyle(head);
hcell.setCellValue("序号");
continue;
}
if (!hcolumn.getIsServerCondition() && !hcolumn.getHidden() && hcolumn.getIsExport()) {
// 创建表头列
HSSFCell hcell = hrow.createCell(h - hempty);
hcell.setCellType(HSSFCell.CELL_TYPE_STRING);
hcell.setCellStyle(head);
hcell.setCellValue(hcolumn.getHeader());
} else {
++hempty;
}
sheet.autoSizeColumn((short) h);
}
// 创建数据列表
for (int b = 1; b <= rowLen; ++b) {
// 创建一行数据列
HSSFRow brow = sheet.createRow(b + titleRowCount);
brow.setHeight((short) 500);
int bempty = 0;
Object obj = objList.get(b - 1);
Row datarow = new Row();
Map dataMap = null;
if(objClass==null) {
dataMap = (Map)obj;
datarow.setId(dataMap.get(query.getKey()).toString());
}else {
Method method = objClass.getMethod(ObjectUtil.methodName(query.getKey(), "get"));
datarow.setId((String) method.invoke(obj));
}
for (int i = 0; i < colLen; ++i) {
Column bcolumn = colList.get(i);
if ("rowIndex".equals(bcolumn.getKey())) {
// 设置当前列序号
HSSFCell bcell = brow.createCell(i);
bcell.setCellType(HSSFCell.CELL_TYPE_STRING);
bcell.setCellStyle(intNumber);
bcell.setCellValue(b);
continue;
}
String data = "";
if(objClass!=null) {
data = ObjectUtil.ObjectToString(obj, bcolumn.getKey(), bcolumn.getFormat());
}else {
if(Date.class.getName().equals(bcolumn.getClassType())||"date".equals(bcolumn.getClassType().toLowerCase())) {
data = bcolumn.getFormat()!=null ? DateUtil.format((Date)dataMap.get(bcolumn.getKey().toUpperCase()), bcolumn.getFormat())
: DateUtil.format((Date)dataMap.get(bcolumn.getKey().toUpperCase()));
}else {
if(dataMap.get(bcolumn.getKey().toUpperCase())!=null) {
data = dataMap.get(bcolumn.getKey().toUpperCase()).toString();
}
}
}
if (!bcolumn.getIsServerCondition() && !bcolumn.getHidden() && bcolumn.getIsExport()) {
// 设置当前列值
HSSFCell bcell = brow.createCell(i - bempty);
// 单元格对齐
if (bcolumn.getAlign().equals("center"))
bcell.setCellStyle(center);
else if (bcolumn.getAlign().equals("left"))
bcell.setCellStyle(huanHang);
// 单元格赋值
if (bcolumn.getClassType().equals("java.lang.Double") && bcolumn.getSuffix() == null) {
bcell.setCellStyle(number);
bcell.setCellValue(Double.parseDouble(data));
} else if (bcolumn.getClassType().equals("java.lang.Integer")) {
bcell.setCellStyle(intNumber);
bcell.setCellValue(Integer.parseInt(data));
} else if (bcolumn.getSuffix() != null) {
bcell.setCellValue(data + "%");
} else {
bcell.setCellValue(data);
}
} else {
++bempty;
}
sheet.autoSizeColumn((short) i);
}
}
//创建sheet完成后,根据前台参数执行 行合并、列合并、多表头等操作
if(queryCondition.getSheetMethod()!=null){
String sheetMethod=queryCondition.getSheetMethod();
String[] params=sheetMethod.split("_");
//正文内容考试的行号索引
int contentBeginIndex=1;
//含有副标题
if(queryCondition.getSheetSubTitle()!=null)
contentBeginIndex=2;
if(params[0].equals("rowSpan")){
for(int i=1;i>>>>>>>>>>>>>>>>>>>>>>>>>>"+((new Date()).getTime()-s)/1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fOut.flush();
fOut.close();
} catch (Exception e2) {
}
}
}
JXL方式写Excel
/*
* JXL方式数据导出 支持多 sheet导出,支持表名、sheet页、标题、副标题单独命名
* 2014-5-6性能优化
*/
@SuppressWarnings({ "unchecked", "deprecation" })
@RequestMapping(value = "/exportSheetsDataJXL")
public void exportSheetsDataJXL(String reqObjs, String tableName, HttpServletRequest request,
HttpServletResponse response) throws Exception {
long s=(new Date()).getTime();
long s1=0;
List queryConditions = JSON.parseArray(reqObjs, QueryCondition.class);
int conCount = queryConditions.size();
String tempfile = System.currentTimeMillis()+"";
OutputStream fOut = new FileOutputStream(request.getRealPath("/") + "templates" + File.separator + "temp" + File.separator + tempfile + ".xls");
// 产生工作簿对象
WritableWorkbook workbook=Workbook.createWorkbook(fOut);
// 定义标题样式
WritableFont titleFont = new WritableFont(WritableFont.TIMES,18,WritableFont.BOLD,false);
WritableCellFormat title=new WritableCellFormat(titleFont);
title.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
title.setAlignment(jxl.format.Alignment.CENTRE);
title.setBorder(Border.BOTTOM,BorderLineStyle.THIN);
title.setBorder(Border.LEFT,BorderLineStyle.THIN);
title.setBorder(Border.RIGHT,BorderLineStyle.THIN);
title.setBorder(Border.TOP,BorderLineStyle.THIN);
title.setWrap(true);
// 定义表头样式
WritableFont headfont = new WritableFont(WritableFont.TIMES,12,WritableFont.BOLD,false);;
WritableCellFormat head = new WritableCellFormat(headfont);
head.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
head.setAlignment(jxl.format.Alignment.CENTRE);
head.setBorder(Border.BOTTOM,BorderLineStyle.THIN);
head.setBorder(Border.LEFT,BorderLineStyle.THIN);
head.setBorder(Border.RIGHT,BorderLineStyle.THIN);
head.setBorder(Border.TOP,BorderLineStyle.THIN);
WritableFont font = new WritableFont(WritableFont.TIMES,12,WritableFont.NO_BOLD,false);;
// 定义居中样式
WritableCellFormat center = new WritableCellFormat(font);
center.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
center.setAlignment(jxl.format.Alignment.CENTRE);
center.setBorder(Border.BOTTOM,BorderLineStyle.THIN);
center.setBorder(Border.LEFT,BorderLineStyle.THIN);
center.setBorder(Border.RIGHT,BorderLineStyle.THIN);
center.setBorder(Border.TOP,BorderLineStyle.THIN);
// 需要自动换行的样式
WritableCellFormat huanHang = new WritableCellFormat(font);
huanHang.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
huanHang.setAlignment(jxl.format.Alignment.CENTRE);
huanHang.setBorder(Border.BOTTOM,BorderLineStyle.THIN);
huanHang.setBorder(Border.LEFT,BorderLineStyle.THIN);
huanHang.setBorder(Border.RIGHT,BorderLineStyle.THIN);
huanHang.setBorder(Border.TOP,BorderLineStyle.THIN);
huanHang.setWrap(true);
// 定义小数数字样式
NumberFormat nf = new NumberFormat("#.##");
WritableCellFormat number = new WritableCellFormat(nf);
number.setFont(font);
number.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
number.setAlignment(jxl.format.Alignment.CENTRE);
number.setBorder(Border.BOTTOM,BorderLineStyle.THIN);
number.setBorder(Border.LEFT,BorderLineStyle.THIN);
number.setBorder(Border.RIGHT,BorderLineStyle.THIN);
number.setBorder(Border.TOP,BorderLineStyle.THIN);
// 定义整数数字样式
NumberFormat intnf = new NumberFormat("#");
WritableCellFormat intNumber = new WritableCellFormat(intnf);
intNumber.setFont(font);
intNumber.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
intNumber.setAlignment(jxl.format.Alignment.CENTRE);
intNumber.setBorder(Border.BOTTOM,BorderLineStyle.THIN);
intNumber.setBorder(Border.LEFT,BorderLineStyle.THIN);
intNumber.setBorder(Border.RIGHT,BorderLineStyle.THIN);
intNumber.setBorder(Border.TOP,BorderLineStyle.THIN);
// 定义居左样式
WritableCellFormat left = new WritableCellFormat(font);
left.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
left.setBorder(Border.BOTTOM,BorderLineStyle.THIN);
left.setBorder(Border.LEFT,BorderLineStyle.THIN);
left.setBorder(Border.RIGHT,BorderLineStyle.THIN);
left.setBorder(Border.TOP,BorderLineStyle.THIN);
left.setWrap(true);
CellView cv = new CellView();
cv.setAutosize(true);
cv.setSize(18);
try {
for (int qindex = 0; qindex < conCount; qindex++) {
/*--------------------------------------------
该段代码为基于xml与反射的数据获取过程 已经略去
-------------------------------------------------*/
s1=(new Date()).getTime();
// 产生工作表对象
WritableSheet sheet = workbook.createSheet(queryCondition.getSheetName() == null ? tableName : queryCondition
.getSheetName(),qindex);
int rowLen = objList.size();
List colList = query.getColumnList();
int colLen = colList.size();
// 创建表头
int hempty = 0;
// 填充标题
Label label=new Label(0,0,queryCondition.getSheetTitle() != null ?queryCondition.getSheetTitle(): queryCondition.getSheetName(),title);
int lastcol = -1;
for (int h = 0; h < colLen; ++h) {
Column hcolumn = colList.get(h);
if (!hcolumn.getIsServerCondition() && !hcolumn.getHidden() && hcolumn.getIsExport()) {
lastcol++;
}
}
sheet.addCell(label);
//sheet.mergeCells(int col1,int row1,int col2,int row2);//左上角到右下角
sheet.setRowView(0,1500);
sheet.mergeCells(0, 0, lastcol,0);
int titleRowCount=1;
//填充副标题
if(queryCondition.getSheetSubTitle()!=null){
titleRowCount=2;
label=new Label(0,1,queryCondition.getSheetSubTitle(),left);
sheet.addCell(label);
sheet.setRowView(1,600,false);
sheet.mergeCells(0,1,lastcol,1);
}
sheet.setRowView(titleRowCount,600,false);
for (int h = 0; h < colLen; ++h) {
Column hcolumn = colList.get(h);
if ("rowIndex".equals(hcolumn.getKey())) {
// 创建序号列
label=new Label(0,titleRowCount,"序号",head);
sheet.addCell(label);
sheet.setColumnView(0,cv);
continue;
}
if (!hcolumn.getIsServerCondition() && !hcolumn.getHidden() && hcolumn.getIsExport()) {
// 创建表头列
label=new Label(h - hempty,titleRowCount,hcolumn.getHeader(),head);
sheet.addCell(label);
sheet.setColumnView(h-hempty,cv);
} else {
++hempty;
}
}
// 创建数据列表
for (int b = 1; b <= rowLen; ++b) {
// 创建一行数据列
sheet.setRowView(b+titleRowCount, 500);
int bempty = 0;
Object obj = objList.get(b - 1);
Row datarow = new Row();
Map dataMap = null;
if(objClass==null) {
dataMap = (Map)obj;
datarow.setId(dataMap.get(query.getKey()).toString());
}else {
Method method = objClass.getMethod(ObjectUtil.methodName(query.getKey(), "get"));
datarow.setId((String) method.invoke(obj));
}
for (int i = 0; i < colLen; ++i) {
Column bcolumn = colList.get(i);
if ("rowIndex".equals(bcolumn.getKey())) {
// 设置当前列序号
jxl.write.Number num=new jxl.write.Number(0,b+titleRowCount,b,intNumber);
sheet.addCell(num);
continue;
}
String data = "";
if(objClass!=null) {
data = ObjectUtil.ObjectToString(obj, bcolumn.getKey(), bcolumn.getFormat());
}else {
if(Date.class.getName().equals(bcolumn.getClassType())||"date".equals(bcolumn.getClassType().toLowerCase())) {
data = bcolumn.getFormat()!=null ? DateUtil.format((Date)dataMap.get(bcolumn.getKey().toUpperCase()), bcolumn.getFormat())
: DateUtil.format((Date)dataMap.get(bcolumn.getKey().toUpperCase()));
}else {
if(dataMap.get(bcolumn.getKey().toUpperCase())!=null) {
data = dataMap.get(bcolumn.getKey().toUpperCase()).toString();
}
}
}
if (!bcolumn.getIsServerCondition() && !bcolumn.getHidden() && bcolumn.getIsExport()) {
// 设置当前列值
WritableCellFormat format=center;
// 单元格对齐
if (bcolumn.getAlign().equals("center")){
format=center;
}
else if (bcolumn.getAlign().equals("left")){
format=left;
}
//bcell.setCellStyle(huanHang);
// 单元格赋值
if (bcolumn.getClassType().equals("java.lang.Double") && bcolumn.getSuffix() == null) {
jxl.write.Number num1=new jxl.write.Number(i-bempty,b+titleRowCount,Double.parseDouble(data),number);
sheet.addCell(num1);
sheet.setColumnView(i-bempty,cv);
} else if (bcolumn.getClassType().equals("java.lang.Integer")) {
jxl.write.Number num2=new jxl.write.Number(i-bempty,b+titleRowCount,Integer.parseInt(data),intNumber);
sheet.addCell(num2);
sheet.setColumnView(i-bempty,cv);
} else if (bcolumn.getSuffix() != null) {
label=new Label(i-bempty,b+titleRowCount,data+"%",format);
sheet.addCell(label);
sheet.setColumnView(i-bempty,cv);
} else {
label=new Label(i-bempty,b+titleRowCount,data,format);
sheet.addCell(label);
sheet.setColumnView(i-bempty,cv);
}
} else {
++bempty;
}
}
}
//创建sheet完成后,根据前台参数执行 行合并、列合并、多表头等操作
if(queryCondition.getSheetMethod()!=null){
String sheetMethod=queryCondition.getSheetMethod();
String[] params=sheetMethod.split("_");
//正文内容考试的行号索引
int contentBeginIndex=1;
//含有副标题
if(queryCondition.getSheetSubTitle()!=null)
contentBeginIndex=2;
if(params[0].equals("rowSpan")){
for(int i=1;i>>>>>>>>>>>>>>>>>>>>>>>>>>"+((new Date()).getTime()-s)/1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fOut.flush();
fOut.close();
} catch (Exception e2) {
}
}
}
其中,JXL合并单元格的方法为
package com.cnpc.framework.utils;
import jxl.Cell;
import jxl.write.WritableSheet;
public final class JXLExcelUtil {
/**
* 合并单元格(行合并),合并规则:内容相同则合并
*
* @param sheet 合并的sheet
* @param colIndex 合并列索引
* @return
*/
@SuppressWarnings("deprecation")
public static WritableSheet rowSpan(WritableSheet sheet, int colindex, int contentBeginIndex) throws Exception {
// 总行数
int rowNum = sheet.getRows();
Cell[] cells = sheet.getRow(1);
// 正文内容应该从第二行开始,第一行为表头的标题
int startRow = contentBeginIndex;
String startValue = "";
for (int i = contentBeginIndex; i <= rowNum; i++) {
cells = sheet.getRow(i);
String value = cells[colindex].getContents();
if (i == contentBeginIndex) {
startValue = value;
continue;
}
if (StrUtil.isNotBlank(startValue) && StrUtil.isNotBlank(value) && startValue.equals(value)) {
if (i == rowNum) //sheet.mergeCells(int col1,int row1,int col2,int row2);//左上角到右下角
sheet.mergeCells(colindex,startRow, colindex,i);
else
continue;
} else {
if((i-1)>startRow)
sheet.mergeCells(colindex,startRow, colindex,i - 1);
startRow = i;
}
startValue=value;
}
return sheet;
}
}