目录
- 什么是 Apache POI?
- 准备工作:添加 POI 依赖
- 核心概念
- 示例 1:读取
.xls格式文件 (HSSFWorkbook - POI 旧版 API) - 示例 2:读取
.xlsx格式文件 (XSSFWorkbook - POI 新版 API) - 示例 3:统一处理
.xls和.xlsx(推荐方式) - 进阶:读取特定 Sheet、特定单元格、处理日期和公式
- 性能优化:使用 SAX 模式读取大文件
- 总结与最佳实践
什么是 Apache POI?
Apache POI (Poor Obfuscation Implementation) 是一个开源的 Java 库,由 Apache 软件基金会维护,它提供了 API,允许程序员使用 Java 代码来操作 Microsoft Office 格式的文件,

- Excel (
.xls,.xlsx) - Word (
.doc,.docx) - PowerPoint (
.ppt,.pptx) - Outlook (
.msg,.pst)
本教程主要关注如何使用 POI 来读取 Excel 文件。
准备工作:添加 POI 依赖
您需要将 POI 库添加到您的项目中,最简单的方式是使用 Maven 或 Gradle。
Maven (pom.xml)
对于较新版本的 POI,它会自动包含所有必需的子模块,推荐使用 poi-ooxml,因为它同时支持 .xls 和 .xlsx。
<dependencies>
<!-- 支持 .xlsx 格式 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version> <!-- 请使用最新稳定版 -->
</dependency>
<!-- 如果只处理 .xls 格式,可以只引入 poi -->
<!-- <dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency> -->
</dependencies>
Gradle (build.gradle)
dependencies {
// 支持 .xlsx 格式
implementation 'org.apache.poi:poi-ooxml:5.2.3' // 请使用最新稳定版
}
核心概念
POI 针对不同版本的 Excel 文件,使用了不同的核心类,理解这一点至关重要。

| Excel 格式 | POI 核心类 | 特点 |
|---|---|---|
.xls (Excel 97-2003) |
HSSFWorkbook |
所有数据都加载到内存中,一个文件一个对象,如果文件很大,会导致内存溢出(OOM)。 |
.xlsx (Excel 2007 及以后) |
XSSFWorkbook |
同样所有数据都加载到内存中,一个文件一个对象,比 .xls 格式更复杂,内存占用也更大。 |
| 通用 | Workbook |
这是一个接口,HSSFWorkbook 和 XSSFWorkbook 都实现了它,推荐通过此接口编程,以实现代码的通用性。 |
| 大文件处理 | SXSSFWorkbook |
流式 API,它只保留当前在屏幕上可见的数据行(默认100行)在内存中,其他数据会临时写入磁盘,这是处理超大 Excel 文件的利器。 |
示例 1:读取 .xls 格式文件
假设我们有一个名为 data.xls 的文件,内容如下:
| 姓名 | 年龄 | 城市 |
|---|---|---|
| 张三 | 25 | 北京 |
| 李四 | 30 | 上海 |
| 王五 | 28 | 广州 |
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadXlsExample {
public static void main(String[] args) {
// 1. 定义文件路径
String filePath = "path/to/your/data.xls"; // 替换为你的文件路径
// try-with-resources 语句可以自动关闭流
try (FileInputStream fis = new FileInputStream(filePath);
// 2. 根据文件输入流创建 HSSFWorkbook 对象
Workbook workbook = new HSSFWorkbook(fis)) {
// 3. 获取第一个工作表 (Sheet)
Sheet sheet = workbook.getSheetAt(0);
// 4. 遍历工作表中的每一行
// 从第一行开始,因为通常第一行是标题
for (Row row : sheet) {
// 5. 遍历行中的每一个单元格
for (Cell cell : row) {
// 6. 根据单元格类型获取值
switch (cell.getCellType()) {
case STRING:
System.out.print(cell.getStringCellValue() + "\t");
break;
case NUMERIC:
// 检查是否是日期类型
if (DateUtil.isCellDateFormatted(cell)) {
System.out.print(cell.getDateCellValue() + "\t");
} else {
System.out.print((int) cell.getNumericCellValue() + "\t"); // 假设年龄是整数
}
break;
case BOOLEAN:
System.out.print(cell.getBooleanCellValue() + "\t");
break;
case FORMULA:
// 获取公式计算后的值
System.out.print(cell.getCellFormula() + " => " + cell.getNumericCellValue() + "\t");
break;
default:
System.out.print("UNKNOWN\t");
}
}
System.out.println(); // 换行
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
示例 2:读取 .xlsx 格式文件
这个过程与读取 .xls 非常相似,只是核心类从 HSSFWorkbook 换成了 XSSFWorkbook。
假设文件名为 data.xlsx同上。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadXlsxExample {
public static void main(String[] args) {
String filePath = "path/to/your/data.xlsx"; // 替换为你的文件路径
try (FileInputStream fis = new FileInputStream(filePath);
// 核心区别:使用 XSSFWorkbook
Workbook workbook = new XSSFWorkbook(fis)) {
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
// 获取值的逻辑与 .xls 完全相同
switch (cell.getCellType()) {
case STRING:
System.out.print(cell.getStringCellValue() + "\t");
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
System.out.print(cell.getDateCellValue() + "\t");
} else {
System.out.print((int) cell.getNumericCellValue() + "\t");
}
break;
case BOOLEAN:
System.out.print(cell.getBooleanCellValue() + "\t");
break;
case FORMULA:
System.out.print(cell.getCellFormula() + " => " + cell.getNumericCellValue() + "\t");
break;
default:
System.out.print("UNKNOWN\t");
}
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
示例 3:统一处理 .xls 和 .xlsx (推荐方式)
在实际开发中,我们无法提前知道用户上传的文件是 .xls 还是 .xlsx,最好的做法是根据文件扩展名动态选择使用 HSSFWorkbook 还是 XSSFWorkbook。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadExcelUnified {
public static void main(String[] args) {
String filePath = "path/to/your/data.xlsx"; // 可以是 .xls 或 .xlsx
Workbook workbook = null;
try (FileInputStream fis = new FileInputStream(filePath)) {
if (filePath.endsWith(".xls")) {
workbook = new HSSFWorkbook(fis);
} else if (filePath.endsWith(".xlsx")) {
workbook = new XSSFWorkbook(fis);
} else {
throw new IllegalArgumentException("不支持的文件类型");
}
Sheet sheet = workbook.getSheetAt(0);
// ... 后续读取逻辑与之前完全相同 ...
for (Row row : sheet) {
for (Cell cell : row) {
switch (cell.getCellType()) {
case STRING:
System.out.print(cell.getStringCellValue() + "\t");
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
System.out.print(cell.getDateCellValue() + "\t");
} else {
System.out.print((int) cell.getNumericCellValue() + "\t");
}
break;
// ... 其他 case
}
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
进阶:读取特定 Sheet、特定单元格、处理日期和公式
获取特定名称的 Sheet
Sheet sheet = workbook.getSheet("Sheet1"); // 通过名称获取
if (sheet == null) {
sheet = workbook.getSheetAt(0); // 如果不存在,获取第一个
}
获取特定单元格 (行和列都是从 0 开始索引)
// 获取第 2 行 (索引为 1)
Row row = sheet.getRow(1);
// 获取该行的第 1 列 (索引为 0)
Cell nameCell = row.getCell(0);
Cell ageCell = row.getCell(1);
String name = nameCell.getStringCellValue();
int age = (int) ageCell.getNumericCellValue();
System.out.println("姓名: " + name + ", 年龄: " + age);
处理日期
POI 将日期存储为 NUMERIC 类型,需要使用 DateUtil.isCellDateFormatted() 来判断。
Cell dateCell = row.getCell(2);
if (dateCell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(dateCell)) {
java.util.Date date = dateCell.getDateCellValue();
System.out.println("日期: " + date);
}
处理公式
如果单元格包含公式,cell.getCellType() 会返回 FORMULA,你可以获取公式字符串本身,或者获取计算后的值。
Cell formulaCell = row.getCell(3);
if (formulaCell.getCellType() == CellType.FORMULA) {
String formula = formulaCell.getCellFormula();
System.out.println("公式: " + formula);
// 获取计算后的值 (前提是已经计算过)
CellValue evaluatedCellValue = workbook.getCreationHelper().createFormulaEvaluator().evaluate(formulaCell);
System.out.println("计算结果: " + evaluatedCellValue.formatAsString());
}
性能优化:使用 SAX 模式读取大文件
对于超大 Excel 文件(例如几百 MB 或上 GB),使用 XSSFWorkbook 会导致内存溢出,这时,必须使用 SXSSFWorkbook(也称为“流式 API”或“BigGridDemo”)。
SXSSFWorkbook 的工作原理是:在内存中只保留一定数量的行(例如100行),当处理新的一行时,最旧的一行会被“刷新”到磁盘上的临时文件中。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class ReadBigExcelWithSAX {
public static void main(String[] args) {
// 创建一个 SXSSFWorkbook,参数 100 表示在内存中保留 100 行
// 如果为 -1,则表示不限制,效果等同于 XSSFWorkbook
Workbook workbook = new SXSSFWorkbook(100);
// ... 接下来的代码与使用 XSSFWorkbook 几乎完全一样 ...
// 创建 Sheet, Row, Cell 并写入数据
// ...
// 写入文件后,一定要清理临时文件!
try {
// 示例:写入文件 (这里只是展示,重点是读取)
// FileOutputStream out = new FileOutputStream("bigfile_output.xlsx");
// workbook.write(out);
// out.close();
// 关闭 workbook,这会触发清理
((SXSSFWorkbook) workbook).dispose();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:SXSSFWorkbook 主要用于写入大文件,因为它能高效地生成文件,对于读取超大文件,POI 提供了基于 SAX 的 XSSF 和 CSV 的事件模型,API 更复杂,但内存占用极小,如果只是读取,且文件真的超大,可以考虑先将 .xlsx 另存为 .csv 格式,然后用逐行读取的方式处理,这是最轻量级的方案。
总结与最佳实践
- 依赖选择:优先使用
poi-ooxml,它同时支持.xls和.xlsx。 - 接口编程:尽量使用
Workbook接口来接收HSSFWorkbook或XSSFWorkbook对象,提高代码的灵活性。 - 资源管理:始终使用
try-with-resources语句来确保FileInputStream和Workbook被正确关闭,防止内存泄漏。 - 动态判断:根据文件扩展名动态创建
HSSFWorkbook或XSSFWorkbook。 - 单元格类型:在读取单元格值之前,务必使用
getCellType()判断其类型,然后进行相应的转换,避免ClassCastException。 - 日期处理:对于
NUMERIC类型的单元格,用DateUtil.isCellDateFormatted()来判断是否为日期。 - 大文件处理:对于写入超大文件,使用
SXSSFWorkbook,对于读取超大文件,考虑转换为 CSV 或使用 POI 的 SAX 事件模型。
希望这份详尽的教程能帮助您掌握 Java POI 读取 Excel 的各种技巧!
