POI百萬數據快速導出excel

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章