一、EasyExcel 简介
EasyExcel 是阿里巴巴开源的 Java Excel 处理工具,支持高效读写 Excel(.xls 和 .xlsx)。其最大特点是极低内存占用,适合大数据量场景,且 API 简洁易用。
二、核心原理
- POI 封装:底层基于 Apache POI,但对大数据量做了优化(如 SAX 解析)。
- 流式处理:写出时一行一行写,读取时一行一行读,避免全部加载到内存。
- 注解驱动:通过注解映射 Java 对象和 Excel 列。
三、常见场景
- 写入 Excel
- 读取 Excel
- 多 Sheet 操作
- 复杂表头/合并单元格
- 自定义样式、公式、图片
四、核心 API 详解
1. 写入 Excel
方式一:简单写入
public class UserData {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("年龄")
private Integer age;
// getter/setter
}
List<UserData> list = ...;
EasyExcel.write("user.xlsx", UserData.class).sheet("用户信息").doWrite(list);
方式二:多 Sheet 写入
ExcelWriter writer = EasyExcel.write("multi.xlsx").build();
WriteSheet sheet1 = EasyExcel.writerSheet(0, "Sheet1").head(UserData.class).build();
WriteSheet sheet2 = EasyExcel.writerSheet(1, "Sheet2").head(UserData.class).build();
writer.write(data1, sheet1);
writer.write(data2, sheet2);
writer.finish();
方式三:自定义样式
HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headStyle, contentStyle);
EasyExcel.write("styled.xlsx", UserData.class)
.registerWriteHandler(styleStrategy)
.sheet("样式")
.doWrite(list);
2. 读取 Excel
方式一:同步读(小数据量)
List<UserData> list = EasyExcel.read("user.xlsx").head(UserData.class).sheet().doReadSync();
方式二:监听器异步读(大数据量)
public class UserDataListener extends AnalysisEventListener<UserData> {
@Override
public void invoke(UserData data, AnalysisContext context) {
// 每读一行回调一次
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 读完回调
}
}
EasyExcel.read("user.xlsx", UserData.class, new UserDataListener()).sheet().doRead();
3. 复杂表头
public class ComplexHeadData {
@ExcelProperty({"一级表头", "姓名"})
private String name;
@ExcelProperty({"一级表头", "年龄"})
private Integer age;
}
4. 合并单元格
// 注册合并策略
EasyExcel.write("merge.xlsx", UserData.class)
.registerWriteHandler(new LoopMergeStrategy(2, 0)) // 每两行合并第一列
.sheet("合并")
.doWrite(list);
五、实战技巧
- 大数据量读写
推荐使用监听器模式,避免内存溢出。可以分批处理数据。 - 自定义转换器
实现 Converter 接口,支持日期、枚举等特殊类型。 - 动态表头
可通过write方法的 head 参数动态生成表头。 - 图片/公式写入
使用ImageWriteHandler、WriteCellData等高级 API。
六、常见问题
- 内存溢出
读写千万级数据时,采用分批处理和监听器模式。 - 多 Sheet 读写
需手动构建 Sheet 并逐个写入/读取。 - 合并单元格不生效
合并策略需注册在写操作中。 - 动态表头不对齐
保证数据和表头顺序一致。
七、常用注解
@ExcelProperty:指定列名、支持多级表头@ExcelIgnore:忽略字段@ColumnWidth:设置列宽@DateTimeFormat:日期格式@NumberFormat:数字格式
八、总结
EasyExcel 是处理 Excel 的利器,适合大数据量、高性能场景。掌握注解映射、监听器模式、样式处理和合并策略,可以应对绝大多数 Excel 读写需求。
九、海量数据导入示例
1、场景说明
假设你有一个 Excel 文件,里面有上百万行数据,需要高效地导入数据库。常规的同步读取会导致内存溢出,推荐使用 EasyExcel 的监听器(AnalysisEventListener)+ 分批入库的方式。
2、准备工作
引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
定义数据模型
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
@Data
public class UserData {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("年龄")
private Integer age;
}
3、监听器实现(分批处理)
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
public class UserDataListener extends AnalysisEventListener<UserData> {
private static final int BATCH_COUNT = 1000; // 每1000条存一次
private List<UserData> cachedDataList = new ArrayList<>(BATCH_COUNT);
// 假设有数据库操作service
private final UserService userService;
public UserDataListener(UserService userService) {
this.userService = userService;
}
@Override
public void invoke(UserData data, AnalysisContext context) {
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
cachedDataList.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理剩余数据
if (!cachedDataList.isEmpty()) {
saveData();
}
}
private void saveData() {
// 批量插入数据库
userService.saveBatch(cachedDataList);
}
}
4、读取 Excel 文件
import com.alibaba.excel.EasyExcel;
public class ImportExcelDemo {
public static void main(String[] args) {
String fileName = "D:/data/huge_data.xlsx";
UserService userService = new UserService(); // 你自己的数据库Service
EasyExcel.read(fileName, UserData.class, new UserDataListener(userService)).sheet().doRead();
}
}
5、补充说明
- 分批入库
推荐每批 500~2000 条,避免单次插入过多造成数据库压力。 - 多线程处理
EasyExcel 读取本身是单线程的,如需多线程入库,可在 saveData() 内异步处理或用线程池。 - 异常处理
可以重写onException方法,记录或处理异常数据。 - 大文件优化
- 尽量不要用同步读取(
doReadSync),容易 OOM。 - 只读需要的 Sheet,不要全表扫描。
- 数据库批量插入要用批处理接口(如 MyBatis 的 batch、JPA 的 saveAll)。
- 尽量不要用同步读取(
6、完整流程总结
- 定义数据模型(对应 Excel 列)
- 实现监听器(分批缓存并批量入库)
- 调用 EasyExcel.read(监听器处理每一行)
- 优化批处理和异常处理
十、海量数据导出示例
1、场景说明
假设你需要将数据库中的百万级数据导出为 Excel 文件。一次性全部读出并写入容易造成内存溢出,推荐分批查询+分批写出,并使用 EasyExcel 的流式写入。
2、准备工作
引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
定义数据模型
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
@Data
public class UserData {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("年龄")
private Integer age;
}
3、分批写出示例
使用 ExcelWriter 分批写出
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
public class ExportExcelDemo {
public static void main(String[] args) {
String fileName = "D:/data/huge_export.xlsx";
ExcelWriter excelWriter = EasyExcel.write(fileName, UserData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("用户数据").build();
int batchSize = 1000; // 每批写1000条
int totalCount = getTotalCount(); // 总数据量
int pageCount = (totalCount + batchSize - 1) / batchSize;
for (int page = 0; page < pageCount; page++) {
List<UserData> batchList = queryData(page, batchSize); // 从数据库查询一批
excelWriter.write(batchList, writeSheet);
}
excelWriter.finish();
}
// 示例:获取总数据量
private static int getTotalCount() {
// 这里应查询数据库,例如 SELECT COUNT(*) FROM user
return 1000000;
}
// 示例:分页查询数据
private static List<UserData> queryData(int page, int batchSize) {
// 这里应查询数据库,例如 LIMIT offset, batchSize
// 这里只演示,实际需替换为你的数据查询逻辑
List<UserData> list = new ArrayList<>();
for (int i = 0; i < batchSize; i++) {
UserData user = new UserData();
user.setName("用户" + (page * batchSize + i));
user.setAge(20 + (i % 30));
list.add(user);
}
return list;
}
}
多 Sheet 分批写出(可选)
如果数据量过大,可以分 Sheet 导出:
ExcelWriter excelWriter = EasyExcel.write(fileName, UserData.class).build();
int sheetSize = 50000; // 每个Sheet 5万条
int totalCount = getTotalCount();
int sheetCount = (totalCount + sheetSize - 1) / sheetSize;
for (int sheetNum = 0; sheetNum < sheetCount; sheetNum++) {
WriteSheet writeSheet = EasyExcel.writerSheet(sheetNum, "Sheet" + (sheetNum + 1)).build();
for (int page = 0; page < sheetSize / batchSize; page++) {
int offset = sheetNum * sheetSize + page * batchSize;
List<UserData> batchList = queryDataByOffset(offset, batchSize);
excelWriter.write(batchList, writeSheet);
}
}
excelWriter.finish();
4、性能优化建议
- 分批查询数据库
不要一次性查出所有数据,避免 OOM。 - ExcelWriter 流式写出
用build()创建 writer,分批写入数据,最后finish()关闭资源。 - 多 Sheet 导出
每个 Sheet 控制在 5~10 万行,便于 Excel 打开。 - 使用临时文件目录
大文件导出时,建议写到磁盘临时目录,再提供下载。 - JVM 内存设置
如需导出超大文件,适当提升 JVM 堆内存。
5、补充说明
- 如果需要自定义样式、合并单元格、插入图片等,可注册相关 WriteHandler。
- 若在 Web 项目中导出,可用
EasyExcel.write(outputStream),写到响应流。

2万+

被折叠的 条评论
为什么被折叠?



