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

如何使用Java POI向同一工作簿添加多工作表?解决调用覆盖问题

解决POI创建多工作表时被覆盖的问题

这问题我之前帮不少开发者踩过坑——核心问题出在每次调用createReportFile时都重新创建了XSSFWorkbook实例,相当于每次都新建一个空白工作簿,之前添加的工作表都属于旧的工作簿对象,根本没被保留下来,最后一次调用写入的是全新的、只有最后一个工作表的工作簿,所以之前的内容自然就没了。

问题根源拆解

看你代码里的workBook = new XSSFWorkbook();,每次执行这行代码都会生成一个全新的工作簿,之前所有的工作表都绑定在旧的工作簿实例上,没有被传递到下一次调用中。最后你写入文件时,只会保存最后一次创建的那个工作簿,所以只剩最后一份报表。

解决方案:复用同一个工作簿实例

我们需要把工作簿的创建和工作表的添加解耦,让所有工作表都添加到同一个工作簿里,最后统一写入文件。下面是具体的代码调整方案:

1. 调整类结构,复用工作簿实例

先把工作簿作为类的成员变量,在初始化时创建一次,而不是每次调用方法都新建:

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.MultiValueMap;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

public class ExcelReporter {
    private XSSFWorkbook workBook;
    private int sheetCount = 0;

    // 初始化工作簿,在第一次生成报表前调用
    public void initWorkbook() {
        workBook = new XSSFWorkbook();
        sheetCount = 0;
    }

    // 修改后的报表创建方法,不再新建工作簿
    public void createReportSheet(MultiValueMap<String, String> mmReport) {
        try {
            // 基于已有的工作簿创建新工作表
            var sheet = workBook.createSheet("REPTRANS_TRN" + sheetCount);
            AtomicInteger rowCounter = new AtomicInteger();

            mmReport.forEach((key, values) -> {
                AtomicInteger cellCounter = new AtomicInteger();
                var row = sheet.createRow(rowCounter.getAndIncrement());
                // 写入键列
                var keyCell = row.createCell(cellCounter.getAndIncrement());
                keyCell.setCellValue(key);
                // 写入值列(支持多值)
                for (String value : values) {
                    var valueCell = row.createCell(cellCounter.getAndIncrement());
                    valueCell.setCellValue(value);
                }
            });
            sheetCount++;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 统一写入文件的方法,所有工作表添加完成后调用
    public void writeToFile(String filePath) {
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            workBook.write(fos);
            System.out.println("多工作表报表生成成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                workBook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2. 调用示例

public class Main {
    public static void main(String[] args) {
        ExcelReporter reporter = new ExcelReporter();
        // 初始化工作簿
        reporter.initWorkbook();

        // 模拟三次调用添加工作表
        MultiValueMap<String, String> report1 = ...; // 你的第一份数据
        MultiValueMap<String, String> report2 = ...; // 你的第二份数据
        MultiValueMap<String, String> report3 = ...; // 你的第三份数据

        reporter.createReportSheet(report1);
        reporter.createReportSheet(report2);
        reporter.createReportSheet(report3);

        // 统一写入文件,只执行一次
        reporter.writeToFile("multi-sheet-report.xlsx");
    }
}

额外场景:给已有Excel追加工作表

如果需要给已经存在的Excel文件添加新工作表,可以调整方法为先读取已有工作簿,再添加新表后保存:

public void appendSheetToExistingExcel(String existingFilePath, MultiValueMap<String, String> mmReport) {
    try {
        // 读取已存在的工作簿
        var fis = new FileInputStream(existingFilePath);
        XSSFWorkbook workBook = new XSSFWorkbook(fis);
        fis.close();

        // 添加新工作表
        var sheet = workBook.createSheet("REPTRANS_TRN" + sheetCount);
        // 写入逻辑和之前一致...

        // 写入回原文件
        try (FileOutputStream fos = new FileOutputStream(existingFilePath)) {
            workBook.write(fos);
        }
        workBook.close();
        sheetCount++;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

关键注意事项

  • 不要在每次添加工作表时都新建XSSFWorkbook,必须复用同一个实例。
  • 写入文件的操作只需要执行一次,在所有工作表都添加完成后再执行,多次写入会覆盖之前的内容。
  • 记得关闭流和工作簿,避免资源泄漏。

内容的提问来源于stack exchange,提问作者Sanket

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:26:54