Apache POI更新Excel后无法读取正确值问题求助
我明白你遇到的这个头疼问题了——用Apache POI更新带公式的Excel后,明明打开Excel能看到正确的计算值,但用代码读取却拿到0.0,必须手动保存一次才行,连关闭Excel都会弹出保存提示。这其实是POI处理公式计算和文件持久化时的一个常见坑,我来帮你拆解原因并给出解决方案。
问题根源
你调用了evaluator.evaluateAll()确实会触发公式计算,但POI默认不会把计算后的结果作为单元格的缓存值持久化到文件中。当你用代码读取生成的文件时,如果读取逻辑没有主动触发公式重新计算,就会读到单元格初始的旧缓存值(比如你遇到的0.0)。而手动保存Excel时,Office会重新计算公式并更新单元格的缓存值,这就是为什么之后读取能拿到正确结果,而且Excel会提示保存——因为POI生成的文件里公式的缓存状态和Excel自身计算后的状态不一致。
具体解决方案
根据你的需求(是否需要保留Excel中的公式),可以选择以下两种方案:
方案1:保留公式,同时写入正确的缓存值
如果你需要保留Excel里的公式,同时让后续读取能直接拿到计算后的正确值,可以在公式计算后,遍历所有公式单元格,手动更新它们的缓存值。修改你的代码如下:
FileInputStream fis = new FileInputStream(new File("input excel here")); JSONObject rawdatajson = jsonobject.getJSONObject("RawJson"); Workbook workbook = WorkbookFactory.create(fis); Sheet sheet = workbook.getSheetAt(0); Row row1 = sheet.createRow(2); for (int i = 0; i < 100; i++) { Cell cell1 = row1.createCell(i); cell1.setCellValue(rawdatajson.get("line_index_" + i).toString()); } // 强制Excel打开时重新计算公式(可选,但能避免Excel端的问题) workbook.setForceFormulaRecalculation(true); FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); // 遍历所有公式单元格,更新缓存值(保留公式的同时写入计算结果) for (Row row : sheet) { for (Cell cell : row) { if (cell.getCellType() == CellType.FORMULA) { CellValue cellValue = evaluator.evaluate(cell); // 针对XSSF格式,保留公式并更新缓存值 if (cell instanceof XSSFCell) { XSSFCell xssfCell = (XSSFCell) cell; // 设置计算后的数值作为缓存值 xssfCell.getCTCell().addNewV().setVal(String.valueOf(cellValue.getNumberValue())); // 标记缓存值为有效 xssfCell.getCTCell().getF().setT(STCellFormulaType.VALUE); } } } } // 确保所有公式都完成计算 evaluator.evaluateAll(); FileOutputStream fos = new FileOutputStream("create excel path here"); workbook.write(fos); // 注意关闭流的顺序,先关输出流再关输入流 fos.flush(); fos.close(); fis.close(); workbook.close(); System.out.println("Done"); finaljson = readfinalexcel.readcode("created excel path here");
方案2:直接将公式替换为计算后的值(无需保留公式)
如果你的场景不需要保留Excel中的公式,只需要最终的计算结果,可以用evaluateInCell方法直接把公式单元格替换成计算后的实际值:
// 在调用evaluator.evaluateAll()之后添加这段代码 for (Row row : sheet) { for (Cell cell : row) { if (cell.getCellType() == CellType.FORMULA) { // 把公式单元格替换为计算后的数值/文本 evaluator.evaluateInCell(cell); } } }
额外建议:读取时也触发公式计算
如果你的读取代码readfinalexcel.readcode没有主动触发公式计算,即使文件里有正确的公式,也可能读到旧缓存值。建议在读取时也添加公式计算逻辑:
// 读取Excel时的示例代码 Workbook readWorkbook = WorkbookFactory.create(new File("created excel path here")); FormulaEvaluator readEvaluator = readWorkbook.getCreationHelper().createFormulaEvaluator(); // 强制计算所有公式 readEvaluator.evaluateAll(); // 之后再读取单元格值
总结
这个问题的核心是POI生成文件时,公式计算后的结果没有被正确持久化为单元格的缓存值,导致读取时拿到旧数据。通过手动更新缓存值或者在读取时重新计算,就能彻底解决这个问题。
内容的提问来源于stack exchange,提问作者sivateja

