首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JXLS 2型SXSSF变压器

JXLS 2型SXSSF变压器
EN

Stack Overflow用户
提问于 2016-03-26 22:43:03
回答 1查看 4.4K关注 0票数 3

我想使用带有JXLS的SXSSF转换器。我试图以这样的方式编写我的模板,这样我就不会得到“试图在已经写入磁盘的范围内写入一行”的异常。模板捕获已知列(例如“头0")和未知列(以”_dynamic“结尾的列)。动态列的数量可能因运行而异。

如果我将SXSSF窗口配置为大于行数,则没有问题。如果我将窗口设置为少于行数,则会得到“试图写入一行.”。异常。但是,在这两种情况下,工作簿都是创建的。有了足够的窗口大小,就会在结果中包含已知的列(“头0")。在窗口大小不足的情况下,结果中将显示已知的列值(尽管有例外情况),但实际的列文本(同样是“头0")缺失。

我想采用这样的方法,因为行数可以在100,000行中编号,并且我想在必要时将数据刷新到磁盘。

在JXLS中这样做是可能的吗?如果是这样的话,是否有方法可以更改模板,而不必编写任何了解数据的Java代码?

以下是代码:

代码语言:javascript
复制
public class JxlsTest {

@Test
public void sxssfDynamicColumns() throws Exception {
    List<Map<String, Object>> lotsOfStuff = createLotsOfStuff();

    Context context = new PoiContext();
    context.putVar("lotsOStuff", lotsOfStuff);
    context.putVar("columns", new Columns());

    try (InputStream in = getClass().getClassLoader().getResourceAsStream("stuff_sxssf_template.xlsx")) {
        try (OutputStream os = new FileOutputStream("stuff_sxssf_out.xlsx")) {
            Workbook workbook = WorkbookFactory.create(in);
            PoiTransformer transformer = PoiTransformer.createSxssfTransformer(workbook, 5, false);

            AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
            List<Area> xlsAreaList = areaBuilder.build();
            Area xlsArea = xlsAreaList.get(0);
            xlsArea.applyAt(new CellRef("Result!A1"), context);
            SXSSFWorkbook workbook2 = (SXSSFWorkbook) transformer.getWorkbook();
            workbook2.write(os);
        }
    }
}

private List<Map<String, Object>> createLotsOfStuff() {
    Map<String, Object> stuff1 = new LinkedHashMap<>();
    Map<String, Object> stuff2 = new LinkedHashMap<>();

    stuff1.put("header0", "stuff_1_value0");
    stuff1.put("header1_dynamic", "stuff_1_value1");
    stuff1.put("header2_dynamic", "stuff_1_value2");
    stuff1.put("header3_dynamic", "stuff_1_value3");

    stuff2.put("header0", "stuff_2_value0");
    stuff2.put("header1_dynamic", "stuff_2_value1");
    stuff2.put("header2_dynamic", "stuff_2_value2");
    stuff2.put("header3_dynamic", "stuff_2_value3");

    return Arrays.asList(stuff1, stuff2);
}

}

和支持的“列”实用程序:

代码语言:javascript
复制
public class Columns {

public Collection<String> keyOf(List<Map<String, Object>> row) {
    return row.get(0).keySet().stream().filter(k -> k.endsWith("_dynamic")).collect(Collectors.toList());
}

public Collection<Object> valueOf(Map<String, Object> row) {
    return row.entrySet().stream()
            .filter(entry -> entry.getKey() != null && entry.getKey().endsWith("_dynamic"))
            .map(Entry::getValue)
            .collect(Collectors.toList());
}

}

模板:

具有适当SXSSF窗口的输出(出现通知头0):

SXSSF窗口不足的输出(未出现通知头0):

来自SXXF窗口不足的错误:

代码语言:javascript
复制
18:33:20.653 [main] DEBUG org.jxls.area.XlsArea - Applying XlsArea at Result!A1
18:33:20.693 [main] ERROR org.jxls.area.XlsArea - Failed to transform Template!B1 into Result!B1
java.lang.IllegalArgumentException: Attempting to write a row[0] in the range [0,0] that is already written to disk.
    at org.apache.poi.xssf.streaming.SXSSFSheet.createRow(SXSSFSheet.java:115) ~[poi-ooxml-3.12.jar:3.12]
    at org.jxls.transform.poi.PoiTransformer.transform(PoiTransformer.java:112) ~[jxls-poi-1.0.8.jar:na]
    at org.jxls.area.XlsArea.transformTopStaticArea(XlsArea.java:232) [jxls-2.2.9.jar:na]
    at org.jxls.area.XlsArea.applyAt(XlsArea.java:134) [jxls-2.2.9.jar:na]

UPDATE我发现如果删除动态标题(请参阅屏幕截图中的B4单元格中的模板),异常不会抛出,一切正常。因此,首先评估行相关模板,然后返回JXLS来评估动态标头模板。有什么方法可以让JXLS首先评估头模板吗?

EN

回答 1

Stack Overflow用户

发布于 2019-09-22 20:11:14

问题是Jxls分两个阶段处理静态单元格。

第一阶段发生在应用命令之前,它只处理顶部命令行之前的单元格。

第二阶段在处理所有命令之后触发,并尝试处理所有剩余的静态单元格。

第二阶段不能很好地处理SXSSF工作簿,因为它不能更新在命令处理期间写入的单元格之前的静态单元格。

现在有了固定物提交给Jxls master分支,如果使用流转换器,它应该通过不同的处理静态单元来解决这个问题。也请参阅相关的Jxls第160期

修补程序应该在即将发布的Jxls 2.7.0中发布。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36241753

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档