You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java写入Excel文件时列表数据仅存入最后一列 原因及修复方法

问题根因

仅最后一条数据留存的核心原因是writeExcel()方法的设计逻辑存在缺陷:

  1. 该方法每次被调用时,都会重新从磁盘读取当前已存储的Excel文件,初始化全新的Workbook实例
  2. 写入时直接调用sh.createRow(r)创建行,POI的createRow()方法会直接清空指定行号下的所有已有单元格内容,不会保留之前写入的数据
  3. 写完当前单条单元格数据后,立刻将只包含当前写入内容的Workbook覆盖写回磁盘

循环写入时的实际执行逻辑:

  • 第1次写入(0,0)位置:读初始文件,写"Test Case",存盘,文件仅存该单元格内容
  • 第2次写入(0,1)位置:重新读取磁盘上仅存(0,0)内容的文件,调用createRow(0)清空第0行所有内容,写"Coupon ID",存盘,(0,0)内容已丢失
  • 后续第3、4次写入逻辑完全一致,每次都会覆盖掉上一次写入的内容,最终执行完成后仅剩下最后一次写入的单元格数据,和实际运行截图效果一致。

当前代码运行得到的最终Excel输出效果

另外原代码还存在流关闭顺序不规范的问题:在写入输出流之前就提前关闭了输入流,极端场景下会触发文件锁异常。

修复方案

推荐方案:复用Workbook实例,全量写入完成后统一存盘

该方案性能最优,避免频繁磁盘IO,是POI操作Excel的标准写法:

  1. 将文件读取、Workbook初始化逻辑从writeExcel()方法中移出,在ExcelHandler类实例化时仅执行一次加载,整个操作周期复用同一个Workbook对象
  2. 修改writeExcel()逻辑:写入行前先判断目标行是否已存在,存在则通过getRow()获取已有行实例,不存在才调用createRow()新建,避免清空已有数据
  3. 新增统一的存盘关流方法,等所有数据遍历、写入全部完成后,再执行一次文件写入、流关闭操作。

修复后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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:06:30