POI 简介
Apache POI 是一个开源的 Java 库,用于操作 Microsoft Office 格式的文件,如 Word、Excel、PowerPoint 等,对于 Excel,POI 提供了两种主要的 API:

HSSF(Horrible Spreadsheet Format): 用于操作 Excel 97-2003 格式的文件(.xls),这种格式最多支持 65536 行和 256 列。XSSF(XML Spreadsheet Format): 用于操作 Excel 2007 及以上版本的格式(.xlsx),这种格式基于 OpenXML 标准,支持更大的行数和列数(理论上是 1048576 行 x 16384 列)。
强烈推荐使用 XSSF,因为 .xlsx 是目前的主流格式,性能和兼容性都更好。
添加 Maven 依赖
你需要在你的 pom.xml 文件中添加 POI 的依赖,为了保持简洁,我们只添加 XSSF 的核心依赖。
<dependencies>
<!-- 使用 XSSFWorkbook 操作 .xlsx 文件 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version> <!-- 建议使用最新稳定版 -->
</dependency>
</dependencies>
基础示例:创建一个简单的 .xlsx 文件
这个例子将演示如何创建一个 Excel 文件,写入一些数据,并保存到本地。
目标: 创建一个名为 student.xlsx 的文件,包含一个工作表 "学生信息",表头有 "学号", "姓名", "年龄",并写入两条数据。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class SimpleExcelWrite {
public static void main(String[] args) {
// 1. 创建一个新的工作簿 (XSSFWorkbook 对应 .xlsx 文件)
Workbook workbook = new XSSFWorkbook();
// 2. 在工作簿中创建一个工作表
Sheet sheet = workbook.createSheet("学生信息");
// 3. 创建行 (行号从 0 开始)
// 创建表头行 (第 0 行)
Row headerRow = sheet.createRow(0);
// 4. 创建单元格并设置表头值
headerRow.createCell(0).setCellValue("学号");
headerRow.createCell(1).setCellValue("姓名");
headerRow.createCell(2).setCellValue("年龄");
// 5. 创建数据行
Row dataRow1 = sheet.createRow(1);
dataRow1.createCell(0).setCellValue(1001);
dataRow1.createCell(1).setCellValue("张三");
dataRow1.createCell(2).setCellValue(20);
Row dataRow2 = sheet.createRow(2);
dataRow2.createCell(0).setCellValue(1002);
dataRow2.createCell(1).setCellValue("李四");
dataRow2.createCell(2).setCellValue(21);
// 6. 设置列宽,让内容显示更美观 (可选)
sheet.autoSizeColumn(0); // 自动调整第一列宽度
sheet.autoSizeColumn(1); // 自动调整第二列宽度
sheet.autoSizeColumn(2); // 自动调整第三列宽度
// 7. 定义输出文件路径
String filePath = "D:/temp/student.xlsx";
// 8. 将工作簿写入文件
try (FileOutputStream fileOut = new FileOutputStream(filePath)) {
workbook.write(fileOut);
System.out.println("Excel 文件生成成功!路径: " + filePath);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 9. 关闭工作簿,释放资源
try {
if (workbook != null) {
workbook.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
代码解释:
Workbook workbook = new XSSFWorkbook();: 创建一个.xlsx格式的工作簿对象。Sheet sheet = workbook.createSheet("学生信息");: 在工作簿中创建一个名为 "学生信息" 的工作表。Row row = sheet.createRow(0);: 在工作表中创建一行,行号从 0 开始。0代表第一行。Cell cell = row.createCell(0);: 在行中创建一个单元格,列号也从 0 开始。cell.setCellValue(...)用于设置单元格的值。sheet.autoSizeColumn(0);: 自动调整指定列的宽度,以适应内容。try-with-resources: 使用try (FileOutputStream ...)可以确保FileOutputStream在代码块执行完毕后自动关闭,避免资源泄漏。workbook.close(): 关闭工作簿,释放内存,对于大型文件,这一步非常重要。
进阶示例:设置样式(字体、颜色、边框等)
在实际应用中,我们经常需要美化 Excel 表格,比如设置标题字体加粗、背景色、添加边框等。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class StyledExcelWrite {
public static void main(String[] args) {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("样式示例");
// 1. 创建一个样式对象
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
// 2. 设置字体
headerFont.setBold(true); // 加粗
headerFont.setFontHeightInPoints((short) 14); // 字号
headerFont.setColor(IndexedColors.WHITE.getIndex()); // 字体颜色
// 3. 将字体应用到样式
headerStyle.setFont(headerFont);
// 4. 设置背景色
headerStyle.setFillForegroundColor(IndexedColors.BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 5. 设置边框
headerStyle.setBorderTop(BorderStyle.THIN);
headerStyle.setBorderBottom(BorderStyle.THIN);
headerStyle.setBorderLeft(BorderStyle.THIN);
headerStyle.setBorderRight(BorderStyle.THIN);
// 6. 创建数据行样式
CellStyle dataStyle = workbook.createCellStyle();
dataStyle.setBorderTop(BorderStyle.THIN);
dataStyle.setBorderBottom(BorderStyle.THIN);
dataStyle.setBorderLeft(BorderStyle.THIN);
dataStyle.setBorderRight(BorderStyle.THIN);
// 7. 应用样式到表头行
Row headerRow = sheet.createRow(0);
String[] headers = {"产品名称", "销量", "单价"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 8. 应用样式到数据行
Object[][] data = {
{"苹果", 150, 5.5},
{"香蕉", 200, 3.8},
{"橙子", 180, 4.2}
};
for (int i = 0; i < data.length; i++) {
Row row = sheet.createRow(i + 1);
for (int j = 0; j < data[i].length; j++) {
Cell cell = row.createCell(j);
cell.setCellValue((Double) data[i][j]); // 假设数据都是数字
cell.setCellStyle(dataStyle);
}
}
// 9. 自动调整列宽
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
// 10. 写入文件
String filePath = "D:/temp/styled_product.xlsx";
try (FileOutputStream fileOut = new FileOutputStream(filePath)) {
workbook.write(fileOut);
System.out.println("带样式的 Excel 文件生成成功!路径: " + filePath);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (workbook != null) {
workbook.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
关键点:
CellStyle和Font: 样式和字体是分开创建的,一个CellStyle可以被多个Cell共享,这样可以提高性能。IndexedColors: POI 提供了一个预定义的颜色常量枚举,方便使用。FillPatternType: 设置填充模式,SOLID_FOREGROUND表示纯色填充。
处理大数据量(SXSSF - 流式 API)
如果需要导出的数据量非常大(比如几十万行),使用 XSSFWorkbook 会导致内存溢出,因为它会将所有数据都加载到内存中。

这时,你应该使用 SXSSF (Streaming Usermodel API),它的工作方式是:将一部分数据(比如100行)保存在内存中,当超过这个数量时,就会将它们临时写入磁盘(一个临时文件),从而释放内存,这个过程对用户是透明的。
SXSSFWorkbook 的核心参数:
rowAccessWindowSize: 内存中保留的行数,超过这个数量的行会被刷新到磁盘。100是一个比较合理的默认值。
示例:使用 SXSSFWorkbook 导出 10 万条数据
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFRow;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;
public class LargeDataExcelWrite {
public static void main(String[] args) {
// 1. 创建 SXSSFWorkbook,指定窗口大小为 100
// -1 表示不使用基于行数的临时文件,而是基于内存使用量
Workbook workbook = new SXSSFWorkbook(100); // 在内存中保留 100 行
Sheet sheet = workbook.createSheet("大数据量示例");
// 2. 创建表头 (和普通 XSSF 一样)
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("ID");
headerRow.createCell(1).setCellValue("随机数");
// 3. 创建大量数据
System.out.println("开始生成数据...");
Random random = new Random();
for (int i = 1; i <= 100000; i++) {
// 注意:SXSSF 的行创建方式略有不同,但通常可以直接使用 Row
Row row = sheet.createRow(i);
row.createCell(0).setCellValue(i);
row.createCell(1).setCellValue(random.nextDouble() * 1000);
// 每 10000 行打印一次进度
if (i % 10000 == 0) {
System.out.println("已生成 " + i + " 行数据...");
}
}
// 4. 写入文件
String filePath = "D:/temp/large_data.xlsx";
try (FileOutputStream fileOut = new FileOutputStream(filePath)) {
workbook.write(fileOut);
System.out.println("大数据量 Excel 文件生成成功!路径: " + filePath);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. 清理临时文件!非常重要!
// SXSSFWorkbook 在关闭时会自动删除临时文件,但显式调用 cleanup() 更安全
if (workbook instanceof SXSSFWorkbook) {
((SXSSFWorkbook) workbook).dispose();
}
try {
if (workbook != null) {
workbook.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
SXSSFWorkbook 的注意事项:
rowAccessWindowSize: 这个参数是性能和内存消耗之间的权衡,值越大,性能越好,但内存消耗也越大。dispose(): 在finally块中调用((SXSSFWorkbook) workbook).dispose()是一个好习惯,它会强制清理所有临时文件,确保没有遗留。- 功能限制:
SXSSF是XSSF的一个子集,不支持所有XSSF的功能,sheet.setRepeatingRows()和sheet.setRepeatingColumns()等。
写入 .xls 文件 (HSSFWorkbook)
如果你需要兼容旧版的 Excel (97-2003),使用 HSSFWorkbook 非常简单,只需替换几行代码即可。
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFCell;
// ... 其他 import 如 FileOutputStream, IOException ...
public class HssfExcelWrite {
public static void main(String[] args) {
// 1. 创建 HSSFWorkbook 对应 .xls 文件
Workbook workbook = new HSSFWorkbook();
// 2. 后续代码和 XSSF 几乎完全一样
Sheet sheet = workbook.createSheet("HSSF 示例");
Row row = sheet.createRow(0);
row.createCell(0).setCellValue("Hello, HSSF!");
String filePath = "D:/temp/hssf_example.xls";
try (FileOutputStream fileOut = new FileOutputStream(filePath)) {
workbook.write(fileOut);
System.out.println(".xls 文件生成成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (workbook != null) {
workbook.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
总结与建议
| 场景 | 推荐的 API | 优点 | 缺点 |
|---|---|---|---|
| 新项目,数据量小 ( < 100,000 行) | XSSFWorkbook |
功能最全,API 成熟,易于使用 | 大数据量时内存消耗大,可能导致 OOM |
| 新项目,数据量大 ( > 100,000 行) | SXSSFWorkbook |
内存占用低,可处理海量数据 | 功能受限,需要清理临时文件 |
需要兼容旧版 Excel (.xls) |
HSSFWorkbook |
兼容性好 | 行列数限制 (65536x256),性能较差 |
最佳实践:
- 优先使用
XSSFWorkbook,除非你有特殊需求。 - 对于大数据导出,必须使用
SXSSFWorkbook。 - 始终使用
try-with-resources或在finally块中关闭Workbook和FileOutputStream,以避免资源泄漏。 - 样式对象 (
CellStyle,Font) 应该被复用,而不是为每个单元格都创建一个新的,这样可以显著提高性能和减少内存占用。
