EasyExcel 是阿里巴巴开源的 Java Excel 处理工具,以事件驱动模式为核心,支持大文件低内存读取、灵活的数据映射及丰富的配置选项,是 Java 生态中处理 Excel 文件的高效选择。
一、前置准备:添加 EasyExcel 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version> <!-- 版本号根据实际情况调整 -->
</dependency>
二、基础读取流程:从文件到内存
EasyExcel 读取 Excel 的核心流程可分为创建读取器→配置参数→注册监听器→执行读取四步:
1. 创建读取器
通过 EasyExcel.read()
方法创建 ExcelReaderBuilder
实例,支持传入文件路径(String) 、文件对象(File) 或输入流(InputStream)
// 方式1:通过文件路径读取(自动关闭流)
ExcelReaderBuilder builder = EasyExcel.read("D:\\data\\user.xlsx");
// 方式2:通过输入流读取(需手动管理流生命周期)
InputStream inputStream = new FileInputStream("D:\\data\\user.xlsx");
ExcelReaderBuilder builder = EasyExcel.read(inputStream);
2. 配置读取参数
通过 ExcelReaderBuilder
配置读取细节,常见参数包括:
sheet :指定读取的 Sheet(可传入索引(0 开始) 、名称(如"Sheet1") 或直接调用 sheet()
默认读取第一个 Sheet);•
headRowNumber:指定表头所在行(默认 0,即第一行为表头);
autoCloseStream:是否自动关闭输入流(默认 true,避免资源泄漏);
excelType :指定 Excel 类型(ExcelTypeEnum.XLS
或 ExcelTypeEnum.XLSX
,可自动识别)。
builder.sheet("用户数据") // 读取名为"用户数据"的Sheet
.headRowNumber(1) // 表头在第二行
.autoCloseStream(true); // 自动关闭流
3. 注册监听器
通过 registerReadListener
方法注册 AnalysisEventListener
实现类,用于处理读取到的数据。监听器包含两个核心方法:
invoke(T data, AnalysisContext context) :每读取一行数据时触发,data
为当前行映射的对象(或 Map
);
doAfterAllAnalysed(AnalysisContext context):所有数据解析完成后触发,用于收尾操作(如关闭数据库连接)。
示例1:监听器处理 Map 数据(无实体类)
builder.registerReadListener(new AnalysisEventListener<Map<Integer, String>>() {
@Override
public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
// 打印每行数据(键为列索引,值为单元格内容)
rowData.forEach((colIndex, value) ->
System.out.print(colIndex + ":" + value + "\t"));
System.out.println();
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("数据读取完毕!");
}
});
示例2:监听器处理实体类数据(推荐)
通过实体类的 @ExcelProperty
注解实现列与字段的映射,提升代码可读性:
// 实体类:定义列映射关系
@Data
public class UserData {
@ExcelProperty("用户ID") // 列名为"用户ID"
private Long userId;
@ExcelProperty("用户名")
private String username;
@ExcelProperty("年龄")
private Integer age;
}
// 监听器:处理实体类数据
builder.registerReadListener(new AnalysisEventListener<UserData>() {
private final List<UserData> dataList = new ArrayList<>();
@Override
public void invoke(UserData userData, AnalysisContext context) {
dataList.add(userData); // 收集数据
if (dataList.size() >= 1000) { // 批量处理(避免内存溢出)
saveToDatabase(dataList);
dataList.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveToDatabase(dataList); // 保存剩余数据
System.out.println("数据读取完毕,共 " + dataList.size() + " 条!");
}
private void saveToDatabase(List<UserData> list) {
// 批量保存到数据库的逻辑(如MyBatis、JPA)
System.out.println("批量保存 " + list.size() + " 条数据");
}
});
4. 执行读取
调用 doRead()
方法启动读取流程(同步执行):
builder.doRead(); // 同步读取(阻塞直到完成)
// 或 builder.doReadSync()(效果相同)
三、进阶用法:灵活配置与优化
1. 动态指定 Sheet
通过 sheet(int index)
或 sheet(String name)
动态选择 Sheet,例如读取第二个 Sheet
EasyExcel.read("D:\\data\\user.xlsx", UserData.class, listener).sheet(1).doRead();
2. 自定义表头行
若 Excel 表头不在第一行,可通过 headRowNumber
指定:
builder.headRowNumber(2); // 表头在第三行
3. 处理额外信息(合并单元格、批注等)
重写监听器的 extra(CellExtra extra, AnalysisContext context)
方法,获取合并单元格、批注等额外信息:
@Override
public void extra(CellExtra extra, AnalysisContext context) {
if (extra.getType() == CellExtraTypeEnum.MERGE) {
System.out.println("合并单元格:起始行 " + extra.getFirstRowIndex() +
",起始列 " + extra.getFirstColumnIndex() +
",结束行 " + extra.getLastRowIndex() +
",结束列 " + extra.getLastColumnIndex());
}
}
4. 批量处理数据(避免内存溢出)
通过监听器收集数据并批量处理(如批量插入数据库),减少内存占用
private static final int BATCH_SIZE = 1000;
private List<UserData> batchList = new ArrayList<>();
@Override
public void invoke(UserData userData, AnalysisContext context) {
batchList.add(userData);
if (batchList.size() >= BATCH_SIZE) {
saveBatch(batchList);
batchList.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if (!batchList.isEmpty()) {
saveBatch(batchList);
}
}
四、注意事项
内存优化:务必通过批量处理(如每 1000 条保存一次)避免内存溢出,尤其处理大文件时;•
异常处理 :监听器中的 invoke
方法抛出异常会终止读取,建议捕获异常并记录日志,而非直接抛出;
流管理 :若未设置 autoCloseStream(true)
,需手动关闭输入流,防止资源泄漏;•
版本兼容:EasyExcel 3.x 版本对 API 进行了优化,建议升级至最新稳定版。
通过以上步骤,可实现 EasyExcel 对 Excel 文件的高效读取,满足不同场景下的数据处理需求
五、无实体类读取excel文件教程
package com.example.util;
import com.alibaba.excel.EasyExcel;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class EasyExcelUtil {
static List<Map<Integer,String>> readExelFile( InputStream fileInputStream){
List<Map<Integer, String>> dataList=new ArrayList<>();
try (InputStream inputStream =fileInputStream) {
// 同步读取 Excel 文件(返回 List<Map<Integer, String>>)
dataList = EasyExcel.read(inputStream, null, null)
.sheet() // 读取默认 Sheet
.headRowNumber(1) // 表头行索引
.doReadSync(); // 同步读取
// 处理返回的数据
for (Map<Integer, String> row : dataList) {
System.out.println("当前行数据:" + row);
// 示例:打印第一列的值
System.out.println("第一列: " + row.get(0));
}
} catch (Exception e) {
e.printStackTrace();
}
return dataList;
}
}
测代码
InputStream inputStream = new FileInputStream("D:\\temp\\1.xlsx");
EasyExcelUtil.readExelFile(inputStream);