如何使用POI分批追加数据到Excel且不读取原有文件?
嘿,这个问题我之前帮不少踩坑的开发者捋清楚过,先给你敲个关键知识点:HSSFWorkbook对应的是旧版的.xls格式,它本身最多只能容纳65536行数据——你要写900万行,远远超出了这个格式的上限,这不仅是内存撑不住的问题,格式本身就不支持。所以第一步必须先调整文件格式,换成支持1048576行的.xlsx格式(对应POI的XSSF/SXSSF),不然根本没法实现需求。
回到你的核心诉求:无需读取原有文件,分批追加行数据。分两种场景给你具体方案:
场景1:从头开始分批写入大数据量(最常见情况)
如果是从零开始生成900万行数据,直接用POI提供的SXSSFWorkbook就可以完美解决内存问题。它是专门为大数据量设计的流式处理类,会把超出内存阈值的行自动写到临时文件,内存里只保留最近的N行,完全不需要加载任何已有数据。
示例代码如下:
// 设置内存中保留1000行,超出的自动写入临时文件 SXSSFWorkbook wb = new SXSSFWorkbook(1000); SXSSFSheet sheet = wb.createSheet("海量数据表"); // 分批写入900万行 for (int rowNum = 0; rowNum < 9000000; rowNum++) { Row row = sheet.createRow(rowNum); // 填充单元格数据,这里以第一列为例 row.createCell(0).setCellValue("数据行" + rowNum); // 每处理10万行手动刷新一次,释放内存(可选,SXSSF会自动处理,但手动触发更可控) if (rowNum % 100000 == 0) { ((SXSSFSheet) sheet).flushRows(); } } // 写入到最终文件 try (FileOutputStream out = new FileOutputStream("海量数据.xlsx")) { wb.write(out); } finally { // 清理临时文件,释放资源 wb.dispose(); }
这种方式的内存占用非常低,完全不用担心OOM问题。
场景2:需要追加到已有的.xlsx文件中
如果确实需要在已存在的xlsx文件末尾追加数据,又不想把整个文件读入内存,可以借助POI的SAX解析能力+OOXML底层操作,只读取工作表的必要结构,不加载整个工作簿到内存,然后直接在XML层面追加新行。
大致实现思路如下:
- 打开已有xlsx的OPCPackage(xlsx本质是zip包)。
- 用SAX解析器读取目标工作表的XML内容,记录当前的最大行号。
- 直接向工作表的XML文件末尾写入新行的XML节点。
- 保存并关闭包。
示例代码框架(需要自定义SAX处理器):
// 打开已有xlsx文件的包 OPCPackage pkg = OPCPackage.open("已有数据.xlsx"); XSSFReader reader = new XSSFReader(pkg); SharedStringsTable sst = reader.getSharedStringsTable(); // 获取目标工作表的输入流(需要先找到对应sheet的rId,可通过reader.getSheetsData()遍历获取) InputStream sheetStream = reader.getSheet("rId1"); // 自定义SAX处理器,用于解析现有sheet并追加行 SheetAppendHandler appendHandler = new SheetAppendHandler(sst, pkg, "目标工作表名"); XMLReader parser = XMLReaderFactory.createXMLReader(); parser.setContentHandler(appendHandler); parser.parse(new InputSource(sheetStream)); // 开始追加新的一批数据 for (int i = 0; i < 100000; i++) { appendHandler.appendRow("追加数据行" + i); } // 保存修改并关闭资源 pkg.save(new FileOutputStream("追加后的数据.xlsx")); pkg.close();
这里的SheetAppendHandler需要你自己实现,继承XSSFSheetXMLHandler,重写相关方法来记录行号,并提供appendRow方法生成新行的XML内容写入到包中。这种方式需要你对OOXML的结构有一定了解,但能彻底避免加载整个文件到内存。
最后再强调一次:绝对不要用HSSF处理超过65536行的数据,格式本身的限制是硬伤,换XSSF/SXSSF才是解决问题的前提。
内容的提问来源于stack exchange,提问作者maurice

