Java写入Excel文件时列表数据仅存入最后一列 原因及修复方法
问题根因
仅最后一条数据留存的核心原因是writeExcel()方法的设计逻辑存在缺陷:
- 该方法每次被调用时,都会重新从磁盘读取当前已存储的Excel文件,初始化全新的Workbook实例
- 写入时直接调用
sh.createRow(r)创建行,POI的createRow()方法会直接清空指定行号下的所有已有单元格内容,不会保留之前写入的数据 - 写完当前单条单元格数据后,立刻将只包含当前写入内容的Workbook覆盖写回磁盘
循环写入时的实际执行逻辑:
- 第1次写入(0,0)位置:读初始文件,写"Test Case",存盘,文件仅存该单元格内容
- 第2次写入(0,1)位置:重新读取磁盘上仅存(0,0)内容的文件,调用
createRow(0)清空第0行所有内容,写"Coupon ID",存盘,(0,0)内容已丢失 - 后续第3、4次写入逻辑完全一致,每次都会覆盖掉上一次写入的内容,最终执行完成后仅剩下最后一次写入的单元格数据,和实际运行截图效果一致。

另外原代码还存在流关闭顺序不规范的问题:在写入输出流之前就提前关闭了输入流,极端场景下会触发文件锁异常。
修复方案
推荐方案:复用Workbook实例,全量写入完成后统一存盘
该方案性能最优,避免频繁磁盘IO,是POI操作Excel的标准写法:
- 将文件读取、Workbook初始化逻辑从
writeExcel()方法中移出,在ExcelHandler类实例化时仅执行一次加载,整个操作周期复用同一个Workbook对象 - 修改
writeExcel()逻辑:写入行前先判断目标行是否已存在,存在则通过getRow()获取已有行实例,不存在才调用createRow()新建,避免清空已有数据 - 新增统一的存盘关流方法,等所有数据遍历、写入全部完成后,再执行一次文件写入、流关闭操作。
修复后ExcelHandler核心代码
public class ExcelHandler { private FileInputStream file; private XSSFWorkbook wb; private String inFilePath; // 构造方法初始化时仅加载一次Excel文件 public ExcelHandler(String filePath) throws IOException { this.inFilePath = filePath; file = new FileInputStream(new File(inFilePath)); wb = new XSSFWorkbook(file); } // 写入方法仅操作内存中的Workbook,不做磁盘读写 public void writeExcel(String sheetName, int r, int c, String data) { Sheet sh = wb.getSheet(sheetName); // 优先获取已有行,避免清空行内已存数据 Row row = sh.getRow(r) != null ? sh.getRow(r) : sh.createRow(r); row.createCell(c).setCellValue(data); } // 所有写入完成后统一调用该方法存盘关流 public void saveAndClose() throws IOException { // 先关闭输入流再写入输出,避免文件锁冲突 file.close(); FileOutputStream outputStream = new FileOutputStream(inFilePath); wb.write(outputStream); wb.close(); outputStream.close(); } // 其余原有方法保持不变 public void readExcelData(String sheetName) { // 原有业务逻辑 } public void createSheet() { // 原有业务逻辑 } public int getRowCountWhenNull(String sheetName) { // 原有业务逻辑 return 0; } }
修复后调用方代码
package Sterling.oms; import Sterling.oms.Process.CouponValidationProcess; import Sterling.oms.Utilities.ExcelHandler; import java.util.ArrayList; public class FuncVerify { public static void main(String args[]) throws Exception { String filePath = System.getProperty("user.dir") + "/src/test/resources/TestData/test.xlsx"; ExcelHandler excelHandler = new ExcelHandler(filePath); excelHandler.readExcelData("Validation"); CouponValidationProcess couponValidationProcess = new CouponValidationProcess("OMS-T781"); excelHandler.createSheet(); String sheet = "ValidationData"; if (excelHandler.getRowCountWhenNull(sheet) < 1) { ArrayList<String> header = new ArrayList<>(); header.add("Test Case"); header.add("Coupon ID"); header.add("Grand Total"); header.add("Manual Grand Total"); for (int i = 0; i < header.size(); i++) { excelHandler.writeExcel(sheet, 0, i, header.get(i)); } } // 所有写入操作完成后统一存盘 excelHandler.saveAndClose(); } }
备选方案(不推荐)
如果业务强制要求每次写入单元格都重载文件,需要修改writeExcel()的行获取逻辑,禁止直接调用createRow()覆盖已有行,同时调整流关闭顺序,但该方案每次写入都要做磁盘IO,数据量大时性能极差,仅适合极小批量写入场景。
内容的提问来源于stack exchange,提问作者Priyom saha
相关产品推荐
相关产品推荐

