我们经常抱怨POI导出非常缓慢,就是我们绞尽脑汁去修改调整还是很慢!那是因为我们没有认真思考过为什么POI导出慢?
1、在大数据量的时候导出为什么慢,难道仅仅因为数据量大,POI的性能问题?当然不是,其实很多时候是因为我们自身代码,也存在问题。导出慢很大一部分原因在频繁的创建对象,设置对象的属性!
其中最重要的是设置单元格的格式:
CellStyle style2 = workbook.createCellStyle();
如果有20万行数据 ,一共有10列,那等于我要创建200万个对象,这么庞大的对象创建怎么能不卡啦!
那有没有办法让只创建一次对象啦!下面我们就来说代码:
1、使用的依赖包
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml-schemas -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>com.github.virtuald</groupId>
<artifactId>curvesapi</artifactId>
<version>1.06</version>
</dependency>
2、创建测试使用的实体类。
@Data
public class TransferRecordVo {
private String transferId;
private Integer userId;
private String userName;
private Integer currencyId;
private String currencyName;
private Double qty;
private Integer transferType;
private String typeStr;
private Date createTime;
}
3、创建工具类(只展示方法)
public ByteArrayOutputStream exportExcel2007Two(String title, String[] headers, Collection<T> dataset, ByteArrayOutputStream out,
String pattern,String [] cellFormat) {
// 声明一个工作薄
SXSSFWorkbook workbook = new SXSSFWorkbook(500);
// 生成一个表格
Sheet sheet = workbook.createSheet(title);
// 设置表格默认列宽度为15个字节
sheet.setDefaultColumnWidth(20);
// 生成一个样式
CellStyle style = workbook.createCellStyle();
// 设置这些样式
//style.setFillBackgroundColor(new IndexedColors(java.awt.Color.gray));
//style.setFillForegroundColor(new IndexedColors(java.awt.Color.gray));
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
style.setBorderBottom(CellStyle.BORDER_THIN);
style.setBorderLeft(CellStyle.BORDER_THIN);
style.setBorderRight(CellStyle.BORDER_THIN);
style.setBorderTop(CellStyle.BORDER_THIN);
style.setAlignment(CellStyle.ALIGN_CENTER);
// 生成一个字体
Font font = workbook.createFont();
font.setBoldweight(Font.BOLDWEIGHT_BOLD);
font.setFontName("宋体");
font.setColor(IndexedColors.BLACK.getIndex());
font.setFontHeightInPoints((short) 11);
// 把字体应用到当前的样式
style.setFont(font);
// 生成并设置另一个样式
CellStyle style2 = workbook.createCellStyle();
style2.setFillForegroundColor(IndexedColors.WHITE.getIndex());
style2.setFillPattern(CellStyle.SOLID_FOREGROUND);
style2.setBorderBottom(CellStyle.BORDER_THIN);
style2.setBorderLeft(CellStyle.BORDER_THIN);
style2.setBorderRight(CellStyle.BORDER_THIN);
style2.setBorderTop(CellStyle.BORDER_THIN);
style2.setAlignment(CellStyle.ALIGN_CENTER);
style2.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
// 生成另一个字体
Font font2 = workbook.createFont();
font2.setBoldweight(Font.BOLDWEIGHT_NORMAL);
// 把字体应用到当前的样式
style2.setFont(font2);
// 产生表格标题行
Row row = sheet.createRow(0);
Cell cellHeader;
for (int i = 0; i < headers.length; i++) {
cellHeader = row.createCell(i);
cellHeader.setCellStyle(style);
cellHeader.setCellValue(new XSSFRichTextString(headers[i]));
}
// 遍历集合数据,产生数据行
Iterator<T> it = dataset.iterator();
int index = 0;
T t;
Field[] fields;
Field field;
XSSFRichTextString richString;
Pattern p = Pattern.compile("^//d+(//.//d+)?$");
Matcher matcher;
String fieldName;
String getMethodName;
Cell cell;
Class tCls;
Method getMethod;
Object value;
String textValue;
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
while (it.hasNext()) {
index++;
row = sheet.createRow(index);
t = (T) it.next();
// 利用反射,根据JavaBean属性的先后顺序,动态调用getXxx()方法得到属性值
fields = t.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
cell = row.createCell(i);
DataFormat df = workbook.createDataFormat();
style2.setDataFormat(df.getFormat("@"));
//cell.setCellStyle(style2);
field = fields[i];
fieldName = field.getName();
getMethodName = "get" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
try {
tCls = t.getClass();
getMethod = tCls.getMethod(getMethodName, new Class[] {});
value = getMethod.invoke(t, new Object[] {});
// 判断值的类型后进行强制类型转换
textValue = null;
if (value instanceof Date) {
textValue = sdf.format((Date) value);
} else {
// 其它数据类型都当作字符串简单处理
if (value != null) {
textValue = value.toString();
}
}
String str = cellFormat[i];
if (textValue != null) {
if(str.trim().equals("Integer")){
style2.setDataFormat(df.getFormat("#0"));
//cell.setCellStyle(style2);
cell.setCellValue(Integer.parseInt(textValue));
}else if(str.trim().equals("Double")){
style2.setDataFormat(df.getFormat("##0.00000"));
//cell.setCellStyle(style2);
cell.setCellValue(Double.parseDouble(textValue));
}else {
richString = new XSSFRichTextString(textValue);
style2.setDataFormat(df.getFormat("@"));
//cell.setCellStyle(style2);
cell.setCellValue(richString);
}
}else{
if(str.trim().equals("Integer")){
style2.setDataFormat(df.getFormat("#0"));
//cell.setCellStyle(style2);
cell.setCellValue(0);
}else if(str.trim().equals("Double")){
style2.setDataFormat(df.getFormat("##0.00000"));
//cell.setCellStyle(style2);
cell.setCellValue(0);
}else {
//richString = new XSSFRichTextString(textValue);
style2.setDataFormat(df.getFormat("@"));
//cell.setCellStyle(style2);
cell.setCellValue("");
}
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} finally {
// 清理资源
}
cell.setCellStyle(style2);
}
}
try {
workbook.write(out);
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return out;
}
说明:
这里我们设置一个通用的数据行的单元格格式,后面只要跟我们传入的每一列对应的格式,设置单元格格式!
获取对应单元格格式,然后根据格式设置不同的单元格格式,最后设置到单元格格式当中
cell.setCellStyle(style2);
这样既设置了格式,又少创建了几百万个对象。速度当然快!
可以看到包括生成20万数据,只花了10秒钟!已经算是相当快乐!