Java 读取 Excel 文件
-
-
- 一、前置准备:引入依赖
-
- [方案 1:Apache POI(功能全,兼容所有Excel版本)](#方案 1:Apache POI(功能全,兼容所有Excel版本))
- [方案 2:EasyExcel(阿里开源,低内存,推荐大数据量)](#方案 2:EasyExcel(阿里开源,低内存,推荐大数据量))
- [二、方案 1:Apache POI 读取 Excel(通用场景)](#二、方案 1:Apache POI 读取 Excel(通用场景))
-
- [场景 1:读取所有sheet的所有单元格(基础版)](#场景 1:读取所有sheet的所有单元格(基础版))
- [场景 2:读取指定sheet和指定行(精准读取)](#场景 2:读取指定sheet和指定行(精准读取))
- [三、方案 2:EasyExcel 读取 Excel(高性能,推荐大数据量)](#三、方案 2:EasyExcel 读取 Excel(高性能,推荐大数据量))
-
- [步骤 1:定义数据实体(与Excel表头映射)](#步骤 1:定义数据实体(与Excel表头映射))
- [步骤 2:自定义监听器(处理读取到的数据)](#步骤 2:自定义监听器(处理读取到的数据))
- [步骤 3:读取Excel文件(核心代码)](#步骤 3:读取Excel文件(核心代码))
- 四、关键注意事项
-
- [1. 文件路径与权限](#1. 文件路径与权限)
- [2. 版本兼容](#2. 版本兼容)
- [3. 性能优化](#3. 性能优化)
- [4. 日期/数字格式](#4. 日期/数字格式)
- 五、两种方案对比
- 六、常见问题解决
-
- [1. `FileNotFoundException`](#1.
FileNotFoundException) - [2. 内存溢出(OOM)](#2. 内存溢出(OOM))
- [3. 日期读取为数字](#3. 日期读取为数字)
- [1. `FileNotFoundException`](#1.
-
Java 读取 Excel 文件核心依赖 Apache POI (兼容 .xls(Excel 97-2003)和 .xlsx(Excel 2007+))或 EasyExcel(阿里开源,低内存、高性能),以下是两种主流方案的完整实现,覆盖「读取简单单元格、读取指定sheet、读取表头+数据」等场景。
一、前置准备:引入依赖
方案 1:Apache POI(功能全,兼容所有Excel版本)
Maven 依赖(需同时引入 poi 和 poi-ooxml,分别对应 .xls 和 .xlsx):
xml
<dependencies>
<!-- 核心依赖:处理 .xls -->
<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>
<!-- 可选:简化日期格式处理 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
方案 2:EasyExcel(阿里开源,低内存,推荐大数据量)
Maven 依赖(仅需核心包,自动兼容 .xls/.xlsx):
xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version> <!-- 最新版 -->
</dependency>
<!-- 可选:日志依赖(EasyExcel 依赖 slf4j) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
二、方案 1:Apache POI 读取 Excel(通用场景)
场景 1:读取所有sheet的所有单元格(基础版)
java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class POIExcelReader {
public static void main(String[] args) {
String filePath = "D:/test.xlsx"; // 替换为你的Excel文件路径
// 区分 .xls 和 .xlsx
Workbook workbook = null;
try (FileInputStream fis = new FileInputStream(filePath)) {
if (filePath.endsWith(".xlsx")) {
workbook = new XSSFWorkbook(fis); // .xlsx
} else if (filePath.endsWith(".xls")) {
workbook = new HSSFWorkbook(fis); // .xls
} else {
throw new IllegalArgumentException("不支持的Excel格式");
}
// 遍历所有sheet
for (Sheet sheet : workbook) {
System.out.println("===== Sheet名称:" + sheet.getSheetName() + " =====");
// 遍历所有行(跳过表头:从第1行开始,rowNum=1)
for (Row row : sheet) {
// 遍历该行所有单元格
for (Cell cell : row) {
// 获取单元格值(统一格式)
String cellValue = getCellValue(cell);
System.out.print(cellValue + "\t");
}
System.out.println(); // 换行
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 工具方法:统一处理不同类型的单元格值
private static String getCellValue(Cell cell) {
if (cell == null) {
return "";
}
switch (cell.getCellType()) {
case STRING: // 字符串
return cell.getStringCellValue();
case NUMERIC: // 数字/日期
if (DateUtil.isCellDateFormatted(cell)) {
// 日期类型
return cell.getDateCellValue().toString();
} else {
// 数字类型(避免科学计数法)
return String.valueOf(cell.getNumericCellValue());
}
case BOOLEAN: // 布尔值
return String.valueOf(cell.getBooleanCellValue());
case FORMULA: // 公式
return cell.getCellFormula() + " = " + cell.getCachedFormulaResultType();
case BLANK: // 空单元格
return "";
default:
return "";
}
}
}
场景 2:读取指定sheet和指定行(精准读取)
java
// 读取指定sheet(索引从0开始,或按名称)
Sheet sheet = workbook.getSheetAt(0); // 第一个sheet
// 或 Sheet sheet = workbook.getSheet("用户数据"); // 按名称
// 读取指定行(如第2行,rowNum=1)
Row targetRow = sheet.getRow(1);
if (targetRow != null) {
// 读取指定单元格(如第3列,cellNum=2)
Cell targetCell = targetRow.getCell(2);
String value = getCellValue(targetCell);
System.out.println("指定单元格值:" + value);
}
// 遍历有效行(跳过空行)
int lastRowNum = sheet.getLastRowNum(); // 最后一行索引
for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null) {
continue; // 跳过空行
}
// 读取该行单元格
String name = getCellValue(row.getCell(0)); // 第1列:姓名
String age = getCellValue(row.getCell(1)); // 第2列:年龄
System.out.println("姓名:" + name + ",年龄:" + age);
}
三、方案 2:EasyExcel 读取 Excel(高性能,推荐大数据量)
EasyExcel 无需加载整个Excel到内存,适合读取十万级以上数据,核心是通过「监听器」逐行读取。
步骤 1:定义数据实体(与Excel表头映射)
java
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
// 对应Excel的表头:姓名、年龄、手机号
@Data // Lombok注解,自动生成get/set
public class UserExcelDTO {
// value:Excel表头名称,index:列索引(可选)
@ExcelProperty(value = "姓名", index = 0)
private String name;
@ExcelProperty(value = "年龄", index = 1)
private Integer age;
@ExcelProperty(value = "手机号", index = 2)
private String phone;
}
步骤 2:自定义监听器(处理读取到的数据)
java
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
// 自定义监听器,逐行读取数据并存储
public class UserExcelListener extends AnalysisEventListener<UserExcelDTO> {
// 存储读取到的数据
private List<UserExcelDTO> dataList = new ArrayList<>();
// 每读取一行数据触发
@Override
public void invoke(UserExcelDTO user, AnalysisContext context) {
dataList.add(user);
System.out.println("读取到数据:" + user);
// 可在此处批量处理(如每1000条插入数据库)
if (dataList.size() >= 1000) {
handleData(); // 处理数据
dataList.clear(); // 清空
}
}
// 所有数据读取完成后触发
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
handleData(); // 处理剩余数据
System.out.println("Excel读取完成,总数据量:" + dataList.size());
}
// 数据处理逻辑(如插入数据库)
private void handleData() {
if (!dataList.isEmpty()) {
// TODO: 批量插入数据库/业务处理
System.out.println("批量处理" + dataList.size() + "条数据");
}
}
// 获取读取到的所有数据
public List<UserExcelDTO> getDataList() {
return dataList;
}
}
步骤 3:读取Excel文件(核心代码)
java
import com.alibaba.excel.EasyExcel;
import java.util.List;
public class EasyExcelReader {
public static void main(String[] args) {
String filePath = "D:/test.xlsx";
// 初始化监听器
UserExcelListener listener = new UserExcelListener();
// 读取Excel(指定文件路径、实体类、监听器)
EasyExcel.read(filePath, UserExcelDTO.class, listener)
.sheet("用户数据") // 指定sheet名称(可选,默认第一个)
.headRowNumber(1) // 表头行数(默认1行)
.doRead(); // 执行读取
// 获取所有数据
List<UserExcelDTO> dataList = listener.getDataList();
System.out.println("最终读取到的数据:" + dataList);
}
}
四、关键注意事项
1. 文件路径与权限
- 确保文件路径无中文/空格(避免
FileNotFoundException); - 若读取服务器文件,需保证Java进程有文件读取权限(如Linux下
chmod 755)。
2. 版本兼容
.xls(HSSFWorkbook)最大支持65536行,.xlsx(XSSFWorkbook)无行数限制;- EasyExcel 自动兼容两种格式,无需手动区分。
3. 性能优化
- Apache POI 读取大数据量Excel易内存溢出,需用
SXSSFWorkbook(流式读取); - EasyExcel 天生适合大数据量,无需额外配置。
4. 日期/数字格式
- Apache POI 需手动判断日期格式(
DateUtil.isCellDateFormatted); - EasyExcel 可通过
@ExcelProperty(converter = DateConverter.class)自定义格式转换。
五、两种方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Apache POI | 功能全、兼容所有Excel特性 | 大数据量易内存溢出、代码繁琐 | 小数据量、需操作复杂Excel(公式/宏) |
| EasyExcel | 低内存、代码简洁、高性能 | 不支持宏/复杂公式 | 大数据量、普通数据读取(推荐) |
六、常见问题解决
1. FileNotFoundException
- 检查文件路径是否正确(绝对路径/相对路径);
- 检查文件是否被占用(如Excel未关闭)。
2. 内存溢出(OOM)
- Apache POI:改用
SXSSFWorkbook流式读取; - 优先使用 EasyExcel。
3. 日期读取为数字
- Apache POI:通过
DateUtil.isCellDateFormatted判断并转换; - EasyExcel:配置日期转换器。
核心原则:小数据量/复杂Excel用 Apache POI,大数据量/普通读取用 EasyExcel;读取时务必处理空单元格和格式转换,避免空指针/格式错误。