2025年9月,阿里把EasyExcel的GitHub仓库归档了。
这个消息在Java圈子里炸开没多久,它的作者就放出了一个大招——FastExcel,并在同月正式进入Apache孵化器,改名Fesod。
说实话,这个交接完成得相当漂亮。
一波三折的故事
EasyExcel当年为什么火?就是因为POI太难用,数据量大了还OOM。EasyExcel基于SAX流式解析,不用把整个Excel加载到内存,100万行照样跑得稳稳的。
但阿里在2024年11月宣布停更,这下很多人不淡定了——线上跑得好好的库,以后谁来维护?
答案来得很快。FastExcel是EasyExcel原作者离开阿里后重新维护的加强版:
- • 2024年12月:FastExcel 1.0.0发布
- • 2025年8月:FastExcel 1.3.0发布(当前稳定版)
- • 2025年9月:进入Apache孵化器,更名Fesod
关键点:完全兼容EasyExcel,API几乎一模一样,迁移过来就是换个包名的事。还新增了两个实用功能——读取Excel指定行数、Excel直接转PDF。
我有个朋友在电商公司做后端,订单导出动不动几十万行。之前用EasyExcel跑得好好的,得知停更消息后焦虑了一阵子。迁移到FastExcel后,他跟我说:"就改了两个地方,一个依赖一个import,一天搞定,跑了两个月没出过问题。"
这不是个案。FastExcel社区挺活跃,issue响应也快。
怎么选
Apache Fesod是FastExcel进入Apache后的新名字,全称是"Fast Easy Spreadsheet and Other Documents"。现在还处于孵化阶段,但已经有1.3.0稳定版可用。
我的建议是:现在用FastExcel,观望Fesod。两者代码基本一致,等Fesod正式毕业了再切换也不迟。
| | | | |
|---|
| 最新版本 | | | | |
| 维护状态 | | | | |
| 内存占用 | | | | |
| 学习成本 | | | | |
| 生态完善度 | | | | |
| 适用场景 | | | | |
| 推荐度 | | | | |
简单说:新项目直接FastExcel,老项目逐步迁移,别再新建POI项目了。
<!-- FastExcel(当前稳定版) -->
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>1.3.0</version>
</dependency>
快速上手
加依赖:
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>1.3.0</version>
</dependency>
1. 定义实体类
用@ExcelProperty注解映射Excel列,value指定表头名称,index指定列顺序:
public class User {
// index从0开始对应Excel的A列,不写index则按属性字母顺序
@ExcelProperty(value = "编号", index = 0)
private Integer id;
@ExcelProperty(value = "姓名", index = 1)
private String name;
@ExcelProperty(value = "年龄", index = 2)
private Integer age;
// 忽略字段,不写入Excel
@ExcelIgnore
private String password;
}
2. 导出Excel
完整示例,包含响应头设置、防乱码、多个Sheet写入:
@RestController
@RequestMapping("/api/user")
public class UserController {
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
// 设置响应头,防止中文文件名乱码
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("用户列表", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 构建数据
List<User> users = buildUsers();
// 写入Excel,可多次调用sheet()写入多个Sheet
FastExcel.write(response.getOutputStream(), User.class)
.sheet("第一页")
.doWrite(users);
// 写第二个Sheet的示例
// FastExcel.write(response.getOutputStream, User.class)
// .sheet("第二页")
// .doWrite(users2);
}
// 模拟数据,实际从数据库查询
private List<User> buildUsers() {
List<User> users = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
User user = new User();
user.setId(i);
user.setName("用户" + i);
user.setAge(18 + i);
// 密码字段,不写入Excel
user.setPassword("123456");
users.add(user);
}
return users;
}
}
3. 导入Excel
导入需要自定义监听器,FastExcel采用SAX模式逐行读取,不会把整个文件加载到内存:
// 监听器,用于处理每行读取的数据
public class UserImportListener extends AnalysisEventListener<User> {
// 存储读取的数据
private final List<User> users = new ArrayList<>();
// 每解析一行数据都会调用invoke
@Override
public void invoke(User user, AnalysisContext context) {
users.add(user);
System.out.println("解析到数据:" + user);
}
// 全部解析完成后调用
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("共解析:" + users.size() + " 条数据");
}
// 提供外部访问数据的方法
public List<User> getUsers() {
return users;
}
}
接口接收上传文件并解析:
@PostMapping("/import")
public String importUsers(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return "请选择文件";
}
UserImportListener listener = new UserImportListener();
// 读取Excel第一个Sheet
FastExcel.read(file.getInputStream(), User.class, listener)
.sheet()
.doRead();
// 获取解析结果,批量保存到数据库
List<User> users = listener.getUsers();
userService.saveBatch(users);
return "成功导入:" + users.size() + " 条数据";
}
4. 大数据量导入(防止OOM)
数据量大时需要分批处理,避免数据全部堆积在内存:
public class BatchUserImportListener extends AnalysisEventListener<User> {
private static final int BATCH_SIZE = 500; // 每500条处理一次
private final List<User> batch = new ArrayList<>(BATCH_SIZE);
private final UserService userService;
public BatchUserImportListener(UserService userService) {
this.userService = userService;
}
@Override
public void invoke(User user, AnalysisContext context) {
batch.add(user);
// 达到批量大小时写入数据库,然后清空缓冲区
if (batch.size() >= BATCH_SIZE) {
saveBatch();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理遗留的零头数据
if (!batch.isEmpty()) {
saveBatch();
}
}
private void saveBatch() {
userService.saveBatch(new ArrayList<>(batch));
System.out.println("批量写入:" + batch.size() + " 条");
// 清空缓冲区,准备下一批数据
batch.clear();
}
}
总结
选FastExcel,理由很直接:
- • 作者靠谱:EasyExcel原班人马,不是野路子
- • 背靠Apache:虽然还在孵化,但至少不会突然消失
如果你的项目还在用POI或者老版本EasyExcel,强烈建议迁移。好的技术选型,不是选最"权威"的,而是选有人在认真维护、且足够稳定的。
三行代码搞定的事,何必绕那么远。
阅读原文:原文链接
该文章在 2026/4/8 8:59:37 编辑过