java - 在 Java 中以块的形式懒惰地生成 Excel 文档
问题描述
我正在寻找一种以流方式生成非常大的 Excel 文档(动态)的方法,而不会在内存中保存太多中间状态(最好也不在磁盘上)。我拥有的是一个惰性数据流Stream<Data>
,可能包含数十万个Data
对象。我想不断将此数据流转换为写入到OutputStream
. 最终目标不是将 Excel 文档写入磁盘,我想将其流式传输到 HTTP 响应。
我尝试过使用Apache POI (4.0.0),但 POI 及其SXSSFWorkbook的问题是您只能写入OutputStream
一次!即这不起作用:
OutputStream os = ..
Stream<Data> dataStream = ...
SXSSFWorkbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk
Sheet sh = wb.createSheet();
partition(dataStream, 100)
.peek((List<Data> data) -> addRow(sh, data))
.forEach(__ -> wb.write(os));
我在这里尝试做的是将数据流 ( Stream<Data>
) 划分为 100 个块,然后调用addRow
方法(此处未显示)将数据转换为 Excel 行并将其写入Sheet
(称为sh
)。wb.write(..)
如果不是因为在第二次调用时(即当我们到达第二个块时)抛出异常,这实际上应该可以正常工作:
java.io.IOException: Stream closed
at java.io.BufferedWriter.ensureOpen(BufferedWriter.java:116)
at java.io.BufferedWriter.write(BufferedWriter.java:221)
at java.io.Writer.write(Writer.java:157)
at org.apache.poi.xssf.streaming.SheetDataWriter.beginRow(SheetDataWriter.java:213)
at org.apache.poi.xssf.streaming.SheetDataWriter.writeRow(SheetDataWriter.java:203)
at org.apache.poi.xssf.streaming.SXSSFSheet.flushOneRow(SXSSFSheet.java:1876)
at org.apache.poi.xssf.streaming.SXSSFSheet.flushRows(SXSSFSheet.java:1851)
at org.apache.poi.xssf.streaming.SXSSFSheet.flushRows(SXSSFSheet.java:1865)
at org.apache.poi.xssf.streaming.SXSSFWorkbook.flushSheets(SXSSFWorkbook.java:949)
at org.apache.poi.xssf.streaming.SXSSFWorkbook.write(SXSSFWorkbook.java:923)
我尝试了各种黑客攻击,例如:
OutputStream os = ..
Stream<Data> dataStream = ...
SXSSFWorkbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk
Sheet sh = wb.createSheet();
partition(dataStream, 100)
.peek((List<Data> data) -> addRow(sh, data))
.forEach(__ -> {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
wb.write(byteArrayOutputStream);
outputStream.write(byteArrayOutputStream.toByteArray());
});
但这似乎也不起作用。我当然可以做这样的事情:
OutputStream os = ..
Stream<Data> dataStream = ...
SXSSFWorkbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk
Sheet sh = wb.createSheet();
dataStream.forEach(row -> addRow(sh, row));
wb.write(os);
但这种方法的问题在于,在将第一个字节推送到OutputStream
. 这意味着OutputStream
消费者需要在数据开始流式传输之前不必要地等待很长时间。
所以我的问题是:如何在不等待首先生成整个文档的情况下生成包含大量行的 Excel文档?
请注意,Apache POI 不是必需的,如有必要,我很乐意切换到另一个库。
解决方案
推荐阅读
- c# - EF Core 5 是否引入了新的 QueryRootExpression 概念?
- javascript - 为什么在javascript文件中未定义“this”
- javascript - 使用 lodash 展平嵌套对象/数组
- selenium - 在 selenium Java 中避免卡巴斯基警报“连接不受保护”
- sql - 获得池中每个人每月的前 3 名得分
- python - gcd算法看不懂
- python - postgresql 表的原子更新
- odoo - Odoo 多个 qweb 报告共享一篇论文
- javascript - 一旦不再关注任何字段,如何提交具有多个字段的表单?
- javascript - 如何将下拉列表的值放入公式中?