POI的SXSSFWorkbook原理浅析
简介
SXSSF是XSSF的流式api版本,可用来写入大体积的sheet,且不占用过高的内存。SXSSF的实现方式是通过一个滑动窗口去限制可访问的row,而XSSF的所有row都是保存在内存中的。对于旧的row数据,即在窗口之外的row数据会写入到硬盘中,从而降低内存占用。
窗口大小可通过new SXSSFWorkbook(int windowSize)构造方法设置或者通过SXSSFSheet#setRandomAccessWindowSize(int windowSize)设置每个sheet中的窗口大小。
当通过createRow()创建新行并且未刷新的记录总数将超过指定的窗口大小时,索引值最低的行将被刷新,并且无法再通过getRow()访问。
默认的窗口大小为100,由SXSSFWorkbook.DEFAULT_WINDOW_SIZE定义。
当窗口大小设置为-1时表示无限制访问row。这时,所有没有通过flushRows()刷新的行数据都可进行访问。
由SXSSF创建的临时文件必须通过调用回收方法来进行清除。
SXSSFWorkbook默认使用inline strings而不是shared strings table,无需在内存中保存字符串,但可能会生成不兼容的文档。使用shared strings table时在内存中保留所有不重复的字符串。
在某些情况下,SXSSF依然会占用大量的内存,比如:单元格合并,超链接,评论等。
源码解析
在createRow()方法中存在如下关键代码:
SXSSFRow newRow = new SXSSFRow(this);
_rows.put(rownum, newRow);
allFlushed = false;
if(_randomAccessWindowSize >= 0 && _rows.size() > _randomAccessWindowSize) {
try {
//行刷新
flushRows(_randomAccessWindowSize);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
在行数超过_randomAccessWindowSize时,会执行row刷新的操作。
flushRows方法代码:
public void flushRows(int remaining) throws IOException
{
while(_rows.size() > remaining) {
flushOneRow();
}
if (remaining == 0) {
allFlushed = true;
}
}
private void flushOneRow() throws IOException
{
Integer firstRowNum = _rows.firstKey();
if (firstRowNum!=null) {
int rowIndex = firstRowNum.intValue();
SXSSFRow row = _rows.get(firstRowNum);
// Update the best fit column widths for auto-sizing just before the rows are flushed
_autoSizeColumnTracker.updateColumnWidths(row);
//通过_writer写入row数据
_writer.writeRow(rowIndex, row);
_rows.remove(firstRowNum);
lastFlushedRowNumber = rowIndex;
}
}
通过上述代码可知,最后row数据写入了_writer中,_writer的声明如下:
private final SheetDataWriter _writer;
SheetDataWriter的重要属性为:
private final File _fd;
private final Writer _out;
其构造函数
public SheetDataWriter() throws IOException {
//创建临时文件
_fd = createTempFile();
//创建写入流
_out = createWriter(_fd);
}
写入row数据也即向临时文件中写入内容:
/**
* 将row数据写入文件
*
* @param rownum 0-based row number
* @param row a row
*
* @throws IOException If an I/O error occurs
*/
public void writeRow(int rownum, SXSSFRow row) throws IOException {
if (_numberOfFlushedRows == 0)
_lowestIndexOfFlushedRows = rownum;
_numberLastFlushedRow = Math.max(rownum, _numberLastFlushedRow);
_numberOfCellsOfLastFlushedRow = row.getLastCellNum();
_numberOfFlushedRows++;
beginRow(rownum, row);
Iterator<Cell> cells = row.allCellsIterator();
int columnIndex = 0;
while (cells.hasNext()) {
writeCell(columnIndex++, cells.next());
}
endRow();
}
void beginRow(int rownum, SXSSFRow row) throws IOException {
_out.write("<row");
writeAttribute("r", Integer.toString(rownum + 1));
if (row.hasCustomHeight()) {
writeAttribute("customHeight", "true");
writeAttribute("ht", Float.toString(row.getHeightInPoints()));
}
if (row.getZeroHeight()) {
writeAttribute("hidden", "true");
}
if (row.isFormatted()) {
writeAttribute("s", Integer.toString(row.getRowStyleIndex()));
writeAttribute("customFormat", "1");
}
if (row.getOutlineLevel() != 0) {
writeAttribute("outlineLevel", Integer.toString(row.getOutlineLevel()));
}
if(row.getHidden() != null) {
writeAttribute("hidden", row.getHidden() ? "1" : "0");
}
if(row.getCollapsed() != null) {
writeAttribute("collapsed", row.getCollapsed() ? "1" : "0");
}
_out.write(">\n");
this._rownum = rownum;
}
void endRow() throws IOException {
_out.write("</row>\n");
}

浙公网安备 33010602011771号