基于流的EXCEL文件导出,SXSSFWorkbook源码解析
當我們在實現excel導出時,在數據量過大的情況下,總是容易發生內存溢出的情況。我們可以使用POI提供的 SXSSFWorkbook 類來避免內存溢出。
注:基于POI4.10版本源碼
以下是官方文檔對SXSSF包的說明:
SXSSF (package: org.apache.poi.xssf.streaming) is an API-compatible streaming extension of XSSF to be used when very large spreadsheets have to be produced, and heap space is limited. SXSSF achieves its low memory footprint by limiting access to the rows that are within a sliding window, while XSSF gives access to all rows in the document. Older rows that are no longer in the window become inaccessible, as they are written to the disk.
大致翻譯如下:
SXSSF是XSSF的一個與API兼容的流擴展,在需要生成非常大的電子表格時使用,堆空間有限。SXSSF通過限制對滑動窗口中的行的訪問來實現其低內存占用,而XSSF允許訪問文檔中的所有行。當將舊行寫入磁盤時,不再在窗口中的舊行變得不可訪問。
使用示例
// 內存中保持100條數據, 超出的部分刷新到磁盤上SXSSFWorkbook wb = new SXSSFWorkbook(100);Sheet sh = wb.createSheet();for(int rownum = 0; rownum < 1000; rownum++){Row row = sh.createRow(rownum);for(int cellnum = 0; cellnum < 10; cellnum++){Cell cell = row.createCell(cellnum);String address = new CellReference(cell).formatAsString();cell.setCellValue(address);}}// rownum < 900 的數據被刷新到磁盤,不能被隨機訪問for(int rownum = 0; rownum < 900; rownum++){Assert.assertNull(sh.getRow(rownum));}// 最后的100條數據仍然在內存中,可以隨機訪問for(int rownum = 900; rownum < 1000; rownum++){Assert.assertNotNull(sh.getRow(rownum));}FileOutputStream out = new FileOutputStream("d:\\sxssf.xlsx");wb.write(out);out.close();// 從磁盤上釋放臨時文件wb.dispose();臨時文件分析
在wb.write(out)此行斷點,debug運行到此處時,可以在windows路徑C:\Users\ADMINI~1\AppData\Local\Temp\下發現類似以下格式的文件:
此文件就是被刷新到磁盤上的數據臨時文件。此文件是怎么生成的呢?接下來我們就進入到源碼分析的階段。進入方法:
/*** Sreate an Sheet for this Workbook, adds it to the sheets and returns* the high level representation. Use this to create new sheets.** @return Sheet representing the new sheet.*/@Overridepublic SXSSFSheet createSheet(){return createAndRegisterSXSSFSheet(_wb.createSheet());}SXSSFSheet createAndRegisterSXSSFSheet(XSSFSheet xSheet){final SXSSFSheet sxSheet;try{sxSheet=new SXSSFSheet(this,xSheet);}catch (IOException ioe){throw new RuntimeException(ioe);}registerSheetMapping(sxSheet,xSheet);return sxSheet;}再進入new SXSSFSheet(this,xSheet)方法:
public SXSSFSheet(SXSSFWorkbook workbook, XSSFSheet xSheet) throws IOException {_workbook = workbook;_sh = xSheet;_writer = workbook.createSheetDataWriter();setRandomAccessWindowSize(_workbook.getRandomAccessWindowSize());_autoSizeColumnTracker = new AutoSizeColumnTracker(this);}接著進入workbook.createSheetDataWriter()方法,最后我們會發現以下代碼:
public SheetDataWriter() throws IOException {_fd = createTempFile();_out = createWriter(_fd);}由此我們知道,在創建sheet頁時,就創建了臨時文件目錄(即每一個sheet頁都會對應創建一個臨時文件)。那么臨時文件是建立在什么目錄下,是否可以手動修改呢?我們繼續跟進_fd = createTempFile()方法:
public File createTempFile() throws IOException {return TempFile.createTempFile("poi-sxssf-sheet", ".xml");}上面代碼我們可以知道,POI會生成一個前綴為’poi-sxssf-sheet’,后綴為’xml’的臨時文件來存放表格數據的DOM結構。
POI提供了TempFileCreationStrategy接口的默認實現DefaultTempFileCreationStrategy來決定臨時文件生成的目錄:
JAVA_IO_TMPDIR實際上是JVM的系統變量java.io.tmpdir,由此我們可以知道SXSSF默認獲取了JVM的臨時文件目錄來作為自己存放臨時文件的目錄。所以我們可以通過以下三種方式(推薦采用第三種)改變SXSSF臨時文件的目錄:
- 設置JVM系統變量 -Djava.io.tmpdir=xxx 此方法會改變JVM所有的臨時文件目錄。
- 實現TempFileCreationStrategy的接口
- DefaultTempFileCreationStrategy的構造方法提供了 dir參數的構造:
public DefaultTempFileCreationStrategy(File dir) { this.dir = dir; }
重新構造DefaultTempFileCreationStrategy實例傳值給org.apache.poi.util.TempFile類:
臨時文件的壓縮
SXSSF在臨時文件中刷新表數據(每頁一個臨時文件),這些臨時文件的大小可以增長到非常大的值。例如,對于20 MB的CSV數據,臨時XML的大小超過了1000MB。如果臨時文件的大小有問題,可以告訴SXSSF使用gzip壓縮:
SXSSFWorkbook wb = new SXSSFWorkbook(100); TempFile.setTempFileCreationStrategy(new DefaultTempFileCreationStrategy(new File("H:\\Temp"))); //壓縮臨時文件 wb.setCompressTempFiles(true);在SXSSFWorkbook類中有以下判斷,我們可以看出,當設置了以上參數,SXSSF會用GZIP的方式壓縮臨時文件:
protected SheetDataWriter createSheetDataWriter() throws IOException {if(_compressTmpFiles) {return new GZIPSheetDataWriter(_sharedStringSource);}return new SheetDataWriter(_sharedStringSource);}壓縮后的臨時文件如下:
可以看到,原本621MB的臨時文件壓縮后只有42MB。但是采用壓縮顯而易見的會影響到EXCEL導出的性能,期間的權衡應該以真實的業務場景來考慮。
實際上SXSSF所有對DOM文檔的操作都直接映射在了XSSF上,只是在外層提供了刷新磁盤的功能。具體是如何實現的,且聽下回分解。
總結
以上是生活随笔為你收集整理的基于流的EXCEL文件导出,SXSSFWorkbook源码解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pom.xml中依赖的<optional
- 下一篇: 第四届工业大数据创新竞赛-Top1方案