如何使用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
相关产品推荐
相关产品推荐

