解决SXSSF使用时“Attempting to write a row[?] in the range [0,?]that is already written to disk.”异常

在开发中,使用SXSSFWorkbook构建导出excel时,可能会遇到“Attempting to write a row[?] in the range [0,?]that is already written to disk.”的报错情况,如下图所示。

bug

对于这种情况下,需要我们详细分析,首先时这个错误时从哪儿抛出的,通过源码分析,查看到在SXSSFSheet的createRow函数中找到这样一个抛出异常的位置。

1
2
3
4
5
6
7
    ...
if (rownum <= this._writer.getLastFlushedRow()) {
throw new IllegalArgumentException("Attempting to write a row[" + rownum + "] " + "in the range [0," + this._writer.getLastFlushedRow() + "] that is already written to disk.");
} else if (this._sh.getPhysicalNumberOfRows() > 0 && rownum <= this._sh.getLastRowNum()) {
throw new IllegalArgumentException("Attempting to write a row[" + rownum + "] " + "in the range [0," + this._sh.getLastRowNum() + "] that is already written to disk.");
} else {
...

分析这段异常的原因,当前要创建的行小于等于最近已经创建的行时,就会抛出异常。因此这个要求是,我们不能在已经创建行的位置再创建行

究其原因,是在于SXSSFWorkbook的本身实现方式,其本身实现方式在于,不断的将一定行数的表格写入临时文件,最终将所有的临时文件合并起来,这种方式中保证了内存的占用数理想,并且导出的效率也比较理想。

在这种实现中,如果一个行已经写入临时文件了,就不能再修改了,因此在源代码中直接限制了重复创建并写同一栏,并在此抛出异常。

解决方案

1、使用HSSFWorkbook、XSSFWorkbook替代SXSSFWorkbook。这种方式中要么是在HSSFWorkbook中仅支持xls,并且导出的数量有限,并且导出文件效率也较低,内存占用较大;虽然在XSSFWorkbook中,导出效率提高了,使用了xlsx格式,导出数量限制也大大放宽,但是内存占用问题依然没有得到解决。

2、避免在已经创建的行上重新创建行,使用getRow代替重复创建的情况。

:多数情况下,这种情况的出现,都是因为程序行数计数标志出现了重复、计数错误等情况导致的。