Apache POI 是一个开源的 Java 库,用于操作 Microsoft Office 格式文件,包括 Excel,我们将从最基础的用法开始,逐步深入到更复杂和实用的场景。
目录
- 准备工作:添加 POI 依赖
- 基础导出:
.xls格式 (HSSFWorkbook)- 简单示例
- 代码解析
- 推荐使用:
.xlsx格式 (XSSFWorkbook)- 简单示例
- 代码解析
- 进阶功能
- 设置单元格样式(字体、颜色、边框、对齐)
- 合并单元格
- 设置列宽
- 写入大数据量(
SXSSFWorkbook解决内存溢出)
- 完整实用示例
- 总结与建议
准备工作:添加 POI 依赖
你需要在你的项目中添加 Apache POI 的依赖,如果你使用 Maven,在 pom.xml 文件中添加以下依赖:
<dependencies>
<!-- POI 核心依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version> <!-- 建议使用最新稳定版 -->
</dependency>
<!-- 支持 .xlsx 格式 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
</dependencies>
如果你使用 Gradle,在 build.gradle 文件中添加:
implementation 'org.apache.poi:poi:5.2.5' implementation 'org.apache.poi:poi-ooxml:5.2.5'
基础导出:.xls 格式 (HSSFWorkbook)
.xls 是 Excel 97-2003 的格式,使用 HSSFWorkbook 类来操作。注意:.xls 格式最多支持 65536 行和 256 列,对于现代应用来说通常不够用。
简单示例
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import java.io.FileOutputStream;
import java.io.IOException;
public class XlsExportExample {
public static void main(String[] args) {
// 1. 创建一个新的 Excel 工作簿 (对应一个 .xls 文件)
Workbook workbook = new HSSFWorkbook();
// 2. 在工作簿中创建一个工作表
Sheet sheet = workbook.createSheet("员工信息");
// 3. 创建标题行 (第0行)
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("姓名");
headerRow.createCell(1).setCellValue("年龄");
headerRow.createCell(2).setCellValue("部门");
// 4. 创建数据行
Row dataRow1 = sheet.createRow(1);
dataRow1.createCell(0).setCellValue("张三");
dataRow1.createCell(1).setCellValue(28);
dataRow1.createCell(2).setCellValue("研发部");
Row dataRow2 = sheet.createRow(2);
dataRow2.createCell(0).setCellValue("李四");
dataRow2.createCell(1).setCellValue(32);
dataRow2.createCell(2).setCellValue("市场部");
// 5. 设置列宽 (自动调整)
for (int i = 0; i < 3; i++) {
sheet.autoSizeColumn(i);
}
// 6. 将工作簿写入文件
try (FileOutputStream fileOut = new FileOutputStream("D:/temp/employees.xls")) {
workbook.write(fileOut);
System.out.println("Excel 文件 (.xls) 导出成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 7. 关闭工作簿,释放资源
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
代码解析
new HSSFWorkbook(): 创建一个.xls格式的工作簿对象。workbook.createSheet("员工信息"): 创建一个名为 "员工信息" 的工作表。sheet.createRow(0): 在工作表中创建一行,索引从 0 开始。row.createCell(0): 在行中创建一个单元格,索引从 0 开始。cell.setCellValue(...): 设置单元格的值,POI 会自动根据数据类型(字符串、数字、布尔值等)进行处理。sheet.autoSizeColumn(i): 自动调整列宽以适应内容。new FileOutputStream(...): 指定一个输出流,将 Excel 内容写入文件。workbook.write(fileOut): 将工作簿对象写入输出流。workbook.close(): 非常重要,关闭工作簿以释放内存。
推荐使用:.xlsx 格式 (XSSFWorkbook)
.xlsx 是 Office 2007 及以后版本的格式,基于 OpenXML 标准,它使用 XSSFWorkbook 类,支持超过百万行和列,是目前的主流选择。
简单示例
代码结构与 .xls 版本几乎完全相同,只需要将 HSSFWorkbook 替换为 XSSFWorkbook。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class XlsxExportExample {
public static void main(String[] args) {
// 1. 创建一个新的 XLSX 格式的工作簿
Workbook workbook = new XSSFWorkbook();
// 2. 在工作簿中创建一个工作表
Sheet sheet = workbook.createSheet("员工信息");
// 3. 创建标题行
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("姓名");
headerRow.createCell(1).setCellValue("年龄");
headerRow.createCell(2).setCellValue("部门");
// 4. 创建数据行
Row dataRow1 = sheet.createRow(1);
dataRow1.createCell(0).setCellValue("王五");
dataRow1.createCell(1).setCellValue(25);
dataRow1.createCell(2).setCellValue("人事部");
Row dataRow2 = sheet.createRow(2);
dataRow2.createCell(0).setCellValue("赵六");
dataRow2.createCell(1).setCellValue(30);
dataRow2.createCell(2).setCellValue("财务部");
// 5. 设置列宽
for (int i = 0; i < 3; i++) {
sheet.autoSizeColumn(i);
}
// 6. 写入文件
try (FileOutputStream fileOut = new FileOutputStream("D:/temp/employees.xlsx")) {
workbook.write(fileOut);
System.out.println("Excel 文件 (.xlsx) 导出成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 7. 关闭工作簿
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
对于新项目,请优先使用 .xlsx 格式 (XSSFWorkbook)。
进阶功能
1 设置单元格样式
通过 CellStyle 接口,可以设置字体、颜色、边框、对齐方式等。
// 1. 创建样式
CellStyle style = workbook.createCellStyle();
// 2. 设置背景色
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 3. 设置字体
Font font = workbook.createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short) 12);
font.setBold(true);
style.setFont(font);
// 4. 设置边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
// 5. 设置对齐方式
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 6. 应用样式到单元格
Cell cell = headerRow.createCell(0);
cell.setCellValue("标题");
cell.setCellStyle(style);
2 合并单元格
使用 Sheet.addMergedRegion() 方法。
// 合并从 (0, 0) 到 (0, 2) 的单元格,即第一行的前三列
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 2));
// 在合并后的区域的第一个单元格写入数据
headerRow.createCell(0).setCellValue("员工信息总表");
3 设置列宽
// 设置第一列的宽度为 20 个字符宽度 sheet.setColumnWidth(0, 20 * 256); // 自动调整列宽 (根据内容) sheet.autoSizeColumn(0);
4 写入大数据量(SXSSFWorkbook)
当导出数据量非常大(例如几十万行)时,XSSFWorkbook 会将所有数据都加载到内存中,导致 OutOfMemoryError (内存溢出)。
SXSSFWorkbook 是 POI 提供的 XSSF 的流式版本,它通过 临时文件 的方式来解决这个问题,内存中只保留一定数量的行,其余的行会被写入磁盘。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class BigDataExportExample {
public static void main(String[] args) {
// 在内存中保留 100 行,超过的写入临时文件
// 参数100表示窗口大小,即内存中最多保留的行数
Workbook workbook = new SXSSFWorkbook(100);
Sheet sheet = workbook.createSheet("大数据表");
// 模拟写入 100,000 行数据
for (int i = 0; i < 100000; i++) {
Row row = sheet.createRow(i);
row.createCell(0).setCellValue("ID: " + i);
row.createCell(1).setCellValue("数据 " + i);
}
try (FileOutputStream fileOut = new FileOutputStream("D:/temp/big_data.xlsx")) {
workbook.write(fileOut);
System.out.println("大数据 Excel 文件导出成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 清理临时文件 (非常重要!)
((SXSSFWorkbook) workbook).dispose();
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意:
- 使用
SXSSFWorkbook后,不能使用Sheet.autoSizeColumn(),因为它需要遍历所有行来计算宽度,这在流式模式下是不可能的,你需要手动设置列宽。 - 在
finally块中,必须调用((SXSSFWorkbook) workbook).dispose()来删除生成的临时文件。
完整实用示例
结合以上所有功能,导出一个格式化、有标题、有数据的 .xlsx 文件。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class CompleteExcelExport {
public static void main(String[] args) {
// 1. 准备数据
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("张三", 28, "研发部", "zhangsan@example.com"));
employees.add(new Employee("李四", 32, "市场部", "lisi@example.com"));
employees.add(new Employee("王五", 25, "人事部", "wangwu@example.com"));
employees.add(new Employee("赵六", 30, "财务部", "zhaoliu@example.com"));
// 2. 创建工作簿
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("员工信息表");
// 3. 创建样式
// 标题样式
CellStyle titleStyle = createTitleStyle(workbook);
// 表头样式
CellStyle headerStyle = createHeaderStyle(workbook);
// 4. 创建标题行 (合并单元格)
Row titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(30); // 设置行高
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue("公司员工信息");
titleCell.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 4)); // 合并6列
// 5. 创建表头行
Row headerRow = sheet.createRow(1);
String[] headers = {"序号", "姓名", "年龄", "部门", "邮箱"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 6. 写入数据
int rowNum = 2; // 数据从第3行开始
for (int i = 0; i < employees.size(); i++) {
Employee emp = employees.get(i);
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(i + 1);
row.createCell(1).setCellValue(emp.getName());
row.createCell(2).setCellValue(emp.getAge());
row.createCell(3).setCellValue(emp.getDepartment());
row.createCell(4).setCellValue(emp.getEmail());
}
// 7. 设置列宽
sheet.setColumnWidth(0, 5 * 256); // 序号
sheet.setColumnWidth(1, 15 * 256); // 姓名
sheet.setColumnWidth(2, 8 * 256); // 年龄
sheet.setColumnWidth(3, 15 * 256); // 部门
sheet.setColumnWidth(4, 25 * 256); // 邮箱
// 8. 写入文件
try (FileOutputStream fileOut = new FileOutputStream("D:/temp/complete_employees.xlsx")) {
workbook.write(fileOut);
System.out.println("完整示例 Excel 文件导出成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 创建标题样式
private static CellStyle createTitleStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 18);
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
// 创建表头样式
private static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 12);
font.setBold(true);
style.setFont(font);
style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setAlignment(HorizontalAlignment.CENTER);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
// 员工实体类
static class Employee {
private String name;
private int age;
private String department;
private String email;
public Employee(String name, int age, String department, String email) {
this.name = name;
this.age = age;
this.department = department;
this.email = email;
}
// Getters...
public String getName() { return name; }
public int getAge() { return age; }
public String getDepartment() { return department; }
public String getEmail() { return email; }
}
}
总结与建议
| 功能 | 类 | 适用场景 | 备注 |
|---|---|---|---|
| 基础格式 | HSSFWorkbook |
兼容旧版 Excel (.xls) |
最多 65536 行,已不推荐 |
| 现代格式 | XSSFWorkbook |
Office 2007+ (.xlsx) |
支持大数据,内存占用高 |
| 大数据流式 | SXSSFWorkbook |
导出海量数据 (.xlsx) |
内存友好,必须手动设置列宽,用后需 dispose() |
最佳实践:
- 优先选择
.xlsx:除非有特殊的兼容性要求,否则始终使用XSSFWorkbook或SXSSFWorkbook。 - 大数据量用
SXSSFWorkbook:如果预估数据量超过 5 万行,直接使用SXSSFWorkbook,并设置一个合适的窗口大小(如 100 或 500)。 - 善用样式:提前创建好
CellStyle和Font对象,然后重复应用到不同的单元格上,避免重复创建,提高性能。 - 资源管理:始终使用
try-with-resources或在finally块中确保Workbook和FileOutputStream被正确关闭,对于SXSSFWorkbook,不要忘记调用dispose()。 - 性能考虑:在循环中创建对象(如
Row,Cell,CellStyle)是常见的性能瓶颈,尽量在循环外创建样式和字体,在循环内重复使用。
