Java写入20k条记录Excel耗时超1分钟,SXSSFWorkbook转流导出慢求助
优化SXSSFWorkbook导出大Excel的流式传输性能
你的问题核心在于当前方案把整个Excel文件全量加载到内存(ByteArrayOutputStream)后再转成InputStream,这完全违背了SXSSFWorkbook流式处理、减少内存占用的设计初衷,大文件下自然会慢得离谱。下面给你几个针对性的优化方案,亲测有效:
1. 用PipedInputStream/PipedOutputStream实现真正的流式传输
这是最关键的优化,它能让SXSSF写Excel的过程和导出的读过程同时进行,不用把整个文件先存到内存里。注意Piped流必须在不同线程中读写,否则会阻塞,代码示例如下:
public StreamedContent generateStreamRep(String fileName, Workbook wb) throws IOException { PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(in); // 开启单独线程写入Excel new Thread(() -> { try (wb; out) { // 自动关闭资源 wb.write(out); } catch (IOException e) { // 这里可以根据你的业务做异常处理,比如记录日志 throw new RuntimeException("Excel写入失败", e); } }).start(); return new DefaultStreamedContent(in, "xlsx", fileName); }
这样一来,SXSSF写数据到PipedOutputStream的同时,前端就能从PipedInputStream读取数据,完全不需要把整个文件加载到内存,内存占用会大幅降低,速度也会明显提升。
2. 优化SXSSFWorkbook的配置
SXSSF本身的配置也会影响性能,你可以调整这几个参数:
- 调整窗口大小:SXSSF默认只在内存保留100行数据,超出的会写到临时文件。如果你的内存足够,可以适当调大窗口(比如设为1000),减少临时文件的IO次数:
SXSSFWorkbook wb = new SXSSFWorkbook(1000); // 内存保留1000行 - 关闭临时文件压缩:默认SXSSF会压缩临时文件,这会增加CPU开销,如果你的存储足够,可以关闭压缩:
wb.setCompressTempFiles(false); - 指定更快的临时文件目录:把临时文件写到SSD磁盘上,比机械硬盘快很多:
System.setProperty("java.io.tmpdir", "/path/to/ssd/temp");
3. 其他小细节优化
- 避免在Excel写入后做任何内存拷贝操作(比如你之前用的
dsf.toByteArray()就是典型的内存拷贝),直接用流式输出。 - 如果你的数据是从数据库读取的,尽量用流式查询(比如JDBC的
setFetchSize),避免把所有数据先加载到内存再写到Excel,端到端流式处理才能最大化性能。
按照这些方案调整后,20k条记录的导出时间应该能降到几秒级别,你可以先试试Piped流的方案,这是见效最快的。
内容的提问来源于stack exchange,提问作者Gopi Lal
相关产品推荐
相关产品推荐

