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");
    }
posted @ 2025-06-05 16:44  Hekk丶  阅读(224)  评论(0)    收藏  举报