我們經常抱怨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秒鐘!已經算是相當快樂!