Java使用XSSFWorkbook/SXSSFWorkbook优雅实现Excel逐行写入
好的,没问题。针对您提出的“Java使用XSSFWorkbook向Excel逐行写入内容,并采取优雅处理方式”的需求,我将提供一个全面、健壮且符合最佳实践的解决方案。 --- ## 🛠️ 准备工作与环境依赖 在开始编码前,确保您的项目中包含了Apache POI库。`XSSFWorkbook`用于处理 `.xlsx` 格式的Excel文件。 **Maven依赖 (pom.xml)** ```xml <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.5</version> <!-- 推荐使用较新版本 --> </dependency> ``` **Gradle依赖 (build.gradle)** ```gradle implementation 'org.apache.poi:poi-ooxml:5.2.5' ``` > **💡 版本建议:** 推荐使用 5.x 系列的最新版本(如5.2.5),它修复了大量旧版本的bug并提供了更好的性能。避免使用3.x或4.x的过旧版本。 --- ## 🚀 优雅的Excel逐行写入方案 核心思想是**将数据模型、写入逻辑和资源管理分离**,使用**流式API**和**try-with-resources**确保资源正确释放。 ### 1. 构建数据模型 (Model) 首先,定义要写入的数据结构,这有助于代码清晰。 ```java public class Employee { private int id; private String name; private String department; private double salary; // 构造函数、getter、setter 省略 public Employee(int id, String name, String department, double salary) { this.id = id; this.name = name; this.department = department; this.salary = salary; } // ... getter/setter } ``` ### 2. 创建Excel写入器 (Service/Utility) 这是核心的优雅实现,包含以下关键特性: * **try-with-resources**: 确保 `Workbook` 和 `OutputStream` 在任何情况下(包括异常)都会被自动关闭。 * **逐行写入 (Row by Row)**: 使用 `createRow` 和 `createCell` 方法逐行、逐单元格写入。 * **定义样式 (可选)**: 创建一个简单的方法来设置表头样式,使输出更专业。 * **使用SXSSFWorkbook处理大数据**: 如果数据量巨大(超过几千行),建议使用 `SXSSFWorkbook`,它在内存中只保留一部分行,其余写入临时文件,从而避免`OutOfMemoryError`。 ```java import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.List; public class ExcelWriter { private static final int WINDOW_SIZE = 100; // SXSSFWorkbook 内存中保留的行数 /** * 将员工列表写入Excel文件(小数据量使用XSSFWorkbook) * * @param employees 员工数据列表 * @param filePath 输出文件路径,例如 "output/employees.xlsx" * @throws IOException 如果写入失败 */ public static void writeEmployeesToExcel(List<Employee> employees, String filePath) throws IOException { // 1. 使用 try-with-resources 自动关闭 Workbook 和 OutputStream try (Workbook workbook = new XSSFWorkbook(); OutputStream fileOut = new FileOutputStream(filePath)) { // 2. 创建工作表 Sheet sheet = workbook.createSheet("员工信息"); // 3. 创建表头行 Row headerRow = sheet.createRow(0); String[] columns = {"ID", "姓名", "部门", "薪资"}; CellStyle headerStyle = createHeaderCellStyle(workbook); for (int i = 0; i < columns.length; i++) { Cell cell = headerRow.createCell(i); cell.setCellValue(columns[i]); cell.setCellStyle(headerStyle); } // 4. 逐行写入数据 int rowNum = 1; // 从第1行开始(第0行是表头) for (Employee emp : employees) { Row row = sheet.createRow(rowNum++); row.createCell(0).setCellValue(emp.getId()); row.createCell(1).setCellValue(emp.getName()); row.createCell(2).setCellValue(emp.getDepartment()); row.createCell(3).setCellValue(emp.getSalary()); } // 5. 可选: 自动调整列宽 for (int i = 0; i < columns.length; i++) { sheet.autoSizeColumn(i); } // 6. 写入文件流 (FileOutputStream 会在 try-with-resources 中自动关闭) workbook.write(fileOut); } // 自动关闭 workbook 和 fileOut } /** * 将员工列表写入Excel文件(大数据量使用SXSSFWorkbook) * * @param employees 员工数据列表 * @param filePath 输出文件路径,例如 "output/employees_large.xlsx" * @throws IOException 如果写入失败 */ public static void writeEmployeesToExcelLarge(List<Employee> employees, String filePath) throws IOException { // 使用 SXSSFWorkbook,并指定内存中保留的行数 try (SXSSFWorkbook workbook = new SXSSFWorkbook(WINDOW_SIZE); OutputStream fileOut = new FileOutputStream(filePath)) { Sheet sheet = workbook.createSheet("员工信息"); // 创建表头 Row headerRow = sheet.createRow(0); String[] columns = {"ID", "姓名", "部门", "薪资"}; // 注意: SXSSFWorkbook 不推荐使用 CellStyle,会产生大量样式对象,影响性能。 // 如果要设置表头样式,建议在数据写入完成后,使用辅助方法一次性设置。 for (int i = 0; i < columns.length; i++) { Cell cell = headerRow.createCell(i); cell.setCellValue(columns[i]); } // 逐行写入数据(大数据量时,此循环非常高效) int rowNum = 1; for (Employee emp : employees) { Row row = sheet.createRow(rowNum++); row.createCell(0).setCellValue(emp.getId()); row.createCell(1).setCellValue(emp.getName()); row.createCell(2).setCellValue(emp.getDepartment()); row.createCell(3).setCellValue(emp.getSalary()); } workbook.write(fileOut); } // 注意: 销毁 SXSSFWorkbook 时,会删除所有临时文件 } // 辅助方法: 创建表头样式 private static CellStyle createHeaderCellStyle(Workbook workbook) { CellStyle style = workbook.createCellStyle(); Font font = workbook.createFont(); font.setBold(true); font.setFontHeightInPoints((short) 12); style.setFont(font); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); // 其他样式,如边框、色等 return style; } } ``` ### 3. 调用示例 (Main) ```java import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // 准备数据 List<Employee> employees = new ArrayList<>(); employees.add(new Employee(1, "张三", "研发部", 15000.0)); employees.add(new Employee(2, "李四", "市场部", 12000.0)); employees.add(new Employee(3, "王五", "财务部", 13000.0)); try { // 小数据量 ExcelWriter.writeEmployeesToExcel(employees, "output/employees.xlsx"); System.out.println("Excel文件已生成: output/employees.xlsx"); // 模拟大数据量 (例如 10万条) ,需要使用 SXSSFWorkbook List<Employee> largeEmployees = new ArrayList<>(); for (int i = 0; i < 100000; i++) { largeEmployees.add(new Employee(i, "员工" + i, "部门" + (i % 10), 10000 + i)); } ExcelWriter.writeEmployeesToExcelLarge(largeEmployees, "output/employees_large.xlsx"); System.out.println("大数据量Excel文件已生成: output/employees_large.xlsx"); } catch (IOException e) { e.printStackTrace(); } } } ``` --- ## 🗺️ 方案比较与选择 | 特性 | `XSSFWorkbook` | `SXSSFWorkbook` | | :--- | :--- | :--- | | **适用场景** | 数据量较小(< 几千行) | 数据量巨大(> 几千行),需要低内存占用 | | **内存占用** | 高,全部数据在内存中 | 低,仅部分数据在内存,其余落盘 | | **性能** | 写入速度较慢(大数据量时) | 写入速度较快(流式) | | **功能支持** | 完全支持所有POI功能(样式、图表等) | 功能受限(不能读取,不能使用复杂样式) | | **推荐使用** | 测试、报表、配置等小文件 | 日志导出、大数据报表、批量下载等 | | **资源释放** | 常规 `close()` | 需要调用 `dispose()` 释放临时文件 (try-with-resources 可以自动完成) | --- ## ⚠️ 常见陷阱与建议 1. **❌ 陷阱:忘记关闭资源。** 不使用 `try-with-resources`,在发生异常时,`Workbook` 或 `OutputStream` 可能不会关闭,导致文件锁、内存泄漏。**✅ 始终使用 `try-with-resources`。** 2. **❌ 陷阱:大数据量时使用 `XSSFWorkbook`。** 会导致 `OutOfMemoryError`。**✅ 识别场景,使用 `SXSSFWorkbook`。** 3. **❌ 陷阱:在循环中频繁创建 `CellStyle`。** 在一个工作簿中,`CellStyle` 对象数量是有限制的(旧版POI有限制,新版已放宽但依然影响性能)。**✅ 将样式定义为常量,或在循环外预先创建好。** 4. **❌ 陷阱:对 `SXSSFWorkbook` 调用 `autoSizeColumn`。** 该操作需要访问所有行数据,但 `SXSSF` 的流式特性导致部分数据在磁盘上,效率极低。**✅ 对于SXSSF,建议在写入时预估列宽,或直接不自动调整。** 5. **🤔 常见问题:如何写入日期?** ```java Cell cell = row.createCell(0); cell.setCellValue(new Date()); CellStyle dateStyle = workbook.createCellStyle(); CreationHelper createHelper = workbook.getCreationHelper(); dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd")); cell.setCellStyle(dateStyle); ``` --- ## 💎 与开发建议 - **优雅的核心**在于**结构清晰**(分离数据、写入、管理逻辑)和**健壮处理**(使用 `try-with-resources` 保证资源释放)。 - **提前规划**数据量级。如果预判数据可能很大,**直接使用 `SXSSFWorkbook`**,不要等出了问题再替换。 - **关注POI版本更新**,新版本通常会修复内存泄漏和性能问题。 - **编写单元测试**,使用少量的数据(如5行)验证写入功能,再用真实数据量进行压力测试。 ######[AI写代码神器 | 2687点数解答 | 2026-04-23 19:58:57]
- Python 利用 win32com 库:将 Excel 转换为 A4 横向 PDF 并单独打印首列(字节豆包 | 507点数解答 | 2025-03-10 15:48:12)325
- Java 实现复制粘贴功能:打造简易文本复制粘贴程序(GPT | 1497点数解答 | 2024-08-25 09:40:33)424
- 解决数据库“dm.jdbc.driver.dmexception: 数据溢出”问题的全步骤指南(阿里通义 | 598点数解答 | 2024-10-15 15:48:38)326
- 达梦数据库数据溢出问题排查及多维度解决方法(阿里通义 | 403点数解答 | 2024-10-15 15:48:41)1205
- Java使用XSSFWorkbook在现有Excel中追加内容行的方法与示例(阿里通义 | 457点数解答 | 2024-04-12 12:00:50)174
- JSP 实现商品展示模块:分页、搜索、交互与美观界面全搞定!(字节豆包 | 19点数解答 | 2024-12-13 15:00:43)352
- Python 实现球类:精准计算半径、表面积与体积,附输入验证与异常处理!(阿里通义 | 261点数解答 | 2024-11-28 21:19:39)365
- Linux 系统用户、组及文件操作全流程实践(字节豆包 | 1142点数解答 | 2026-03-20 17:29:38)35
- PyCharm 中用 Selenium 编写自动化测试脚本,轻松登录 eShop 测试平台并点击“我的订单”(字节豆包 | 304点数解答 | 2024-11-06 15:38:30)451
- Python:创建文件、统计单词频率并按字母排序输出的实现(GPT | 697点数解答 | 2024-05-30 10:30:24)271
- Linux 系统下切换用户并创建文件的操作指南(字节豆包 | 516点数解答 | 2026-03-20 17:16:28)28
- 51 单片机:定时器 0 实现 8 个 LED 循环点亮,附代码及优化建议(字节豆包 | 1193点数解答 | 2024-12-27 15:10:29)336