0%

poi

Apache POI 是常用的 Java 开发 excel 相关业务功能的工具包.

HSSF: Excel 97 版本, 后缀: .xls
XSSF: Excel 2007 版本, 后缀: .xlsx
SXSSF: 是 Apache POI v3.8+ 之后, 在 XSSF 基础上开发的低内存的流式扩展

1. 常见问题

1.1. 1. excel 数量上限

org.apache.poi.ss.SpreadsheetVersion 中预设了 excle 不同版本的行与列的数量上限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

/**
* Excel97 format aka BIFF8
* <ul>
* <li>The total number of available rows is 64k (2^16)</li>
* <li>The total number of available columns is 256 (2^8)</li>
* <li>The maximum number of arguments to a function is 30</li>
* <li>Number of conditional format conditions on a cell is 3</li>
* <li>Number of cell styles is 4000</li>
* <li>Length of text cell contents is 32767</li>
* </ul>
*/
EXCEL97(0x10000, 0x0100, 30, 3, 4000, 32767),

/**
* Excel2007
*
* <ul>
* <li>The total number of available rows is 1M (2^20)</li>
* <li>The total number of available columns is 16K (2^14)</li>
* <li>The maximum number of arguments to a function is 255</li>
* <li>Number of conditional format conditions on a cell is unlimited
* (actually limited by available memory in Excel)</li>
* <li>Number of cell styles is 64000</li>
* <li>Length of text cell contents is 32767</li>
* <ul>
*/
EXCEL2007(0x100000, 0x4000, 255, Integer.MAX_VALUE, 64000, 32767);

如 excel 2007 最大行数是 2^20, 即每一个 Sheet 最大行数 为1048576.

1.2. 2. 打开 excel 文件内存溢出

如果直接 WorkbookFactory.create(InputStream inputStream) 读取 excel 需要非常大的内存, 容易出现内存溢出的情况

解决方案

  1. 改成读取文件

在官方 API 中有说明, ``WorkbookFactory.create(File file)` 方式读取文件的资源消耗之前方式小.

  1. 控制加载数据量, 流式处理
1
2
3
4
5
<dependency>
<groupId>com.monitorjbl</groupId>
<artifactId>xlsx-streamer</artifactId>
<version>2.0.0</version>
</dependency>
1
2
3
4
Workbook workbook = StreamingReader.builder()
.rowCacheSize(100) //缓存到内存中的行数, 默认是10
.bufferSize(4096) //读取资源时, 缓存到内存的字节大小, 默认是1024
.open(in); //打开资源, 必须, 可以是InputStream或者是File, 注意: 只能打开XLSX格式的文件

1.3. 3. 临时文件

poi 会在执行 write 方法时, 在 /tmp/poifiles 下创建临时文件, 名称形如 poi-sxssf-sheet423173909854689649.xml. 该文件大小依据需要生成文件的数据量决定, 会在线程退出时自动回收.
但如果线程意外退出, 那么该文件就不会回收;

解决方案:

  1. 清空文件数据, 释放资源占用

删除文件, 并重启服务, 可释放空间. 但如果是线上服务, 无法立即重启, 可通过清空文件数据释放磁盘资源占用.
由于 jvm 对临时文件还存在引用, 直接删除文件无法回收存储空间.
通过 echo "" > temp.xml 命令, 将文件内容清空;

  1. 主动清除临时文件

生成文件完成后, 主动调用 dispose 方法清除临时文件

1
2
3
4
5
6
7
8
9
try(Workbook workbook = new SXSSFWorkbook(200);){
// ...
}finally{
if (workbook != null) {
if (workbook instanceof SXSSFWorkbook) {
((SXSSFWorkbook) workbook).dispose();
}
}
}

2. Resources