使用SXSSFWorkbook導出大量excel表格

前言:這幾天遇到一個需求,需要從數據庫讀取記錄然後導出成excel表格,

在這個過程遇到很多問題,從最開始使用HSSFWorkbook到XSSFWorkbook,最後使用

SXSSFWorkbook。


先來了解下爲什麼用SXSSFWorkbook,在源碼中發現屬性名_randomAccessWindowSize,

指定內存中緩存的最大行數,他就是SXSSFWorkbook能導出大量數據關鍵因素, 具體怎

麼實現的呢,一步步查看到SXSSFSheet源碼,在創建行的方法中(加粗部分),當達到設

置的最大行數時就flushRows 操作,把當前內存中行數寫到磁盤中去


    public Row createRow(int rownum) {
        int maxrow = SpreadsheetVersion.EXCEL2007.getLastRowIndex();
        if (rownum >= 0 && rownum <= maxrow) {
            Row previousRow = rownum > 0 ? this.getRow(rownum - 1) : null;
            int initialAllocationSize = 0;
            if (previousRow != null) {
                initialAllocationSize = previousRow.getLastCellNum();
            }

            if (initialAllocationSize <= 0 && this._writer.getNumberOfFlushedRows() > 0) {
                initialAllocationSize = this._writer.getNumberOfCellsOfLastFlushedRow();
            }

            if (initialAllocationSize <= 0) {
                initialAllocationSize = 10;
            }

            SXSSFRow newRow = new SXSSFRow(this, initialAllocationSize);
            this._rows.put(new Integer(rownum), newRow);
            if (this._randomAccessWindowSize >= 0 && this._rows.size() > this._randomAccessWindowSize) {
                try {
                    this.flushRows(this._randomAccessWindowSize);
                } catch (IOException var7) {
                    throw new RuntimeException(var7);
                }
            }

            return newRow;
        } else {
            throw new IllegalArgumentException("Invalid row number (" + rownum + ") outside allowable range (0.." + maxrow + ")");
        }
    }

SXSSFWorkbook已經幫我們解決了一大問題了,接下來又遇到問題,我這次導出數據量在

10萬行左右,列有15列,一次性查詢所有數據的話會造成內存突然大量佔用,某些情況可能

會造成服務奔潰,繼續分段查詢數據


 僞代碼

public int exportExcel(){

	XSSFWorkbook xssfWb = null;
	SXSSFWorkbook sxssfWorkbook = null;
	SXSSFSheet sxssSheet = null;
	FileOutputStream fos = null;
	try {
		//內存緩存最大行數
		int rowMaxCache = 100;

		xssfWb = new XSSFWorkbook();
		//rowMaxCache(可選)不聲明默認100
		sxssfWorkbook = new SXSSFWorkbook(xssfWb, rowMaxCache);
		sxssSheet = (SXSSFSheet) sxssfWorkbook.createSheet("sheet標題名");
		//記錄總數
		int listNum = 查詢數據庫;
		//查詢數據庫最大記錄數
		int sourceMaxCache = 10000;
		//總共查詢次數
		int findNum = 0;
		if (0 == (listNum % sourceMaxCache)){
			findNum = listNum / sourceMaxCache;
		}else {
			findNum = listNum / sourceMaxCache + 1;
		}

		for (int j = 0; j < findNum; j++){
			
			//mapParam作爲查詢條件限制,這裏只設置設置數據庫中的limit
			mapParam.put("start",j * sourceMaxCache + 1);
			mapParam.put("end",sourceMaxCache);
			
			//導出記錄
			List<Map<String,Object>> list = 查詢數據庫操作(mapParam);
			//行數
			int exportNum = list.size();
			//列數
			int columnNum = list.get(0).size();
			
			// 首行 標題
			if (0 == j){
				Row row0 = sxssSheet.createRow(0);
				Cell sxssC1 = row0.createCell(0);
				sxssC1.setCellValue("標題");
				//設置單元格樣式 setStyle
				sxssC1.setCellStyle(setStyle(sxssfWorkbook));
				//合併  參數1:起始行號 參數2:終止行號 參數3:起始列號 參數4:終止列號
				sxssSheet.addMergedRegion(new CellRangeAddress(0, 0, 0, exportNum));
			}
			
			//第二行 表頭
			if (0 == j){
				Row row1 = sxssSheet.createRow(1);
				for (int k = 0; k < columnNum; k++){
					// Row row, int column, String value, CellStyle style
					// 行 ,列號,單元格值,樣式(可選)
					CellUtil.createCell(row1, 0, value, setStyle(sxssfWorkbook));
				}
			}

			//數據
			for (int i = 0; i < exportNum; i++) {
				Row row = sxssSheet.createRow(j * sourceMaxCache + i + 2);
				Map<String, Object> map = list.get(i);
				// 數據
				for (int k = 0; k < columnNum; k++){
					// Row row, int column, String value, CellStyle style
					// 行 ,列號,單元格值
					CellUtil.createCell(row1, 0, value);
				}
			}
			//幫助gc回收內存
			list.clear();
		}
		//創建文件導出文件路徑
		File f = new File(currStr);
		try {
			if (!f.exists()) {
				f.createNewFile();
			}
		} catch (IOException e1) {
			return "0";
		}
		fos = new FileOutputStream(f);
		sxssfWorkbook.write(fos);

	} catch (Exception e) {
		e.printStackTrace();
		return "0";
	}finally{
		try {
			if(fos != null){
				fos.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

maven 引入包

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>3.9</version>
</dependency>

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>3.9</version>
</dependency>

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml-schemas</artifactId>
	<version>3.9</version>
</dependency>

總結,代碼雖然能實現大量數據的excel表格導出,但還有優化問題,

1、時間和空間平衡;爲了解決上面內存佔用問題,我採用了分批次查詢數據庫,來減小服務

器壓力,但這樣有引出另個問題,因爲多次查詢數據庫,造成每次數據寫完excel後會有等待

數據返回時間,而且對sql查詢性能優化顯得也更爲重要。

2、樣式問題:讀者可能會奇怪我除了對錶頭和標題加入樣式,正式數據卻沒有加入樣式,

因爲在我測試過程中,對每個單元加入樣式等其他操作時,會嚴重影響性能,在一番網上查詢

資料未解決後才得以放棄,因爲樓主覺得爲了樣式而犧牲性能有些不值得(畢竟爲每個單元格

設置樣式可是行列相乘)


希望有讀者有更好的方案提出來一起交流,如果發現代碼邏輯

有什麼錯誤也請及時指出


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