Apache Fesod 入门教程:高性能 Excel 处理从 0 到 1(告别 OOM,百万行数据轻松拿捏)
在 Java 企业级开发中,Excel 导入导出 是一个绕不开的”标配需求”——从批量导入用户数据、导出业务报表,到对账文件解析、数据迁移,几乎每个项目都得跟 Excel 打交道。
但传统方案的痛点同样令人头疼:
- Apache POI:基于 DOM 模型,读 10 万行就得吃 2GB+ 内存,动不动就
OutOfMemoryError; - EasyExcel:虽然解决了流式读取的问题,但格式支持有限、社区迭代放缓;
- 手写 SXSSF:API 繁琐,维护成本高,新人上手劝退。
今天给大家介绍的 Apache Fesod(Incubating),正是为解决这些痛点而生的新一代 Excel 处理库。它继承了 EasyExcel 的流式处理基因,底层架构全面重构,性能更猛、API 更优雅,已于 2025 年 9 月正式进入 Apache 孵化器,前途不可限量。
这篇教程带你从 0 基础 → 独立落地,覆盖安装、读、写、高级技巧全套实战,每个示例都可直接复制运行。
0)Apache Fesod 是什么 & 为什么选它
0.1 项目背景 & 定位
Apache Fesod(发音
/ˈfɛsɒd/)是一个高性能、低内存的 Java 电子表格处理库,名字是 “Fast Easy Spreadsheet and Other Documents” 的缩写。
项目前身为 2024 年 10 月开源的 FastExcel,由原 EasyExcel 核心维护团队发起并彻底重构底层。2025 年 9 月 17 日,经 Apache 基金会孵化器投票通过(9 票赞成 / 0 票反对),正式更名为 Fesod 进入 Apache 孵化。
截至目前,GitHub Star 已突破 5K+,累计发布 5 个社区版本,拥有 20+ 贡献者,是近年来 Java Excel 生态中最受关注的新星项目。
0.2 三大核心特性
| 特性 | 说明 |
|---|---|
| 🚀 高性能读写 | 底层基于 SAX 事件驱动 + 零拷贝流式处理,百万行数据内存占用稳定在 50MB 以内,比 POI 快 3~5 倍 |
| 🎯 极简 API | 链式调用 + 注解驱动,3 行代码完成读取,5 行代码完成写入,学习成本极低 |
| 📦 格式全面 | 支持 .xlsx(Excel 2007+)、.xls(Excel 97-2003)、CSV,未来还将扩展 PDF/HTML 导出 |
0.3 常见方案横向对比
| 方案 | 内存模型 | 百万行内存 | API 易用性 | 格式支持 | 社区活跃 |
|---|---|---|---|---|---|
| Apache POI(UserModel) | DOM 全量加载 | 2GB+ | ⭐⭐ 繁琐 | xls/xlsx | ✅ 老牌稳定 |
| Apache POI(SXSSF) | 流式写入 | ~500MB | ⭐ 难用 | xlsx | ✅ |
| EasyExcel | SAX 流式 | ~100MB | ⭐⭐⭐⭐ | xls/xlsx | ⚠️ 放缓 |
| Apache Fesod | SAX + 零拷贝 | ~50MB | ⭐⭐⭐⭐⭐ | xls/xlsx/CSV | 🔥 Apache 孵化中 |
一句话结论:新项目直接上 Fesod,大文件、高并发场景下它就是目前的最优解。
1)快速开始:5 分钟跑通第一个 Demo
1.1 环境要求 & 版本兼容
Fesod 对 JDK 版本的支持非常友好:
| Fesod 版本 | JDK 支持范围 | 备注 |
|---|---|---|
| 1.3.x(最新) | JDK 8 ~ JDK 25 | ✅ 推荐使用 |
| 1.2.x | JDK 8 ~ JDK 21 | |
| 1.1.x | JDK 8 ~ JDK 21 | |
| 1.0.x | JDK 8 ~ JDK 21 |
⚠️ 注意:Fesod 底层仍依赖 POI 解析二进制格式。如果你的项目中已经引入了
poi、poi-ooxml等依赖,需要手动排除冲突版本,建议统一使用 Fesod 传递依赖的 POI 版本。
1.2 Maven / Gradle 引入
Maven(pom.xml) — 最常用:
1 | |
Gradle(build.gradle):
1 | |
引入成功后,先编译一下确认没有版本冲突,就可以进入实战环节了。
2)核心概念扫盲:POJO、监听器、流式 vs 同步
在正式写代码前,先理解 Fesod 的三个核心设计,后续读和写都会围绕它们展开:
2.1 POJO 模型 + 注解映射
Fesod 用 Java Bean + 注解 来描述 Excel 的行列结构,常见注解:
| 注解 | 作用 | 常用属性 |
|---|---|---|
@ExcelProperty("表头名") | 标记字段对应哪一列 | value 表头名;index 列号(从 0 开始) |
@ExcelIgnore | 标记该字段不参与读写 | - |
@DateTimeFormat("yyyy-MM-dd") | 日期格式转换 | value 格式字符串 |
@NumberFormat("#.##%") | 数字格式转换(百分比等) | value 格式字符串 |
2.2 监听器模式(ReadListener)
读取大文件必须用监听器,这是 Fesod 内存友好的核心:
- 数据不会一次性加载到内存,而是每解析一行就回调一次
invoke()方法; - 你在
invoke()里做业务处理(校验、入库、转发等),处理完对象就可以被 GC; doAfterAllAnalysed()会在全部解析完毕后回调,适合做收尾动作(如关闭资源、统计总数)。
2.3 流式 vs 同步读取
| 方式 | API | 适用场景 | 内存占用 |
|---|---|---|---|
| 流式(推荐) | .doRead() + ReadListener | 数据量大(>1万行)、生产环境 | 恒定低内存 |
| 同步 | .doReadSync() 返回 List | 数据量小(<1万行)、单元测试 | 全部加载进内存 |
💡 生产环境永远优先用流式 + 监听器。同步读虽然写起来简单,但大数据量下直接 OOM。
3)实战一:读取 Excel(4 种常用姿势全覆盖)
3.1 准备数据模型 DemoData
先定义一个最简单的用户数据模型:
1 | |
3.2 方式 1:自定义监听器 + 流式读取(最常用 ✅)
先写监听器类,每读到一行就会进 invoke():
1 | |
主程序调用:
1 | |
3.3 方式 2:Lambda 匿名监听器(代码更少)
如果逻辑简单,不想单独写监听器类,直接用 PageReadListener 或匿名内部类:
1 | |
3.4 方式 3:同步读取(小数据量用)
测试场景或者确定行数很少时,直接拿 List:
1 | |
3.5 方式 4:不定义 POJO,直接用 Map 读取
如果 Excel 结构不固定、或者临时写脚本,用 Map<Integer, String> 按列索引读:
1 | |
3.6 进阶:只读取指定列(列过滤)
文件有 20 列但你只关心第 1 列和第 3 列?用 includeColumnIndexes 节省内存:
1 | |
4)实战二:写入 Excel(3 种方式 + 注解详解)
写入比读取更简单,Fesod 提供了 链式 API,一路 .write().sheet().doWrite() 就行。
4.1 最简单的写入(列表直接写)
还是用刚才的 DemoData 类(注意 @ExcelProperty 的表头名):
1 | |
生成的 Excel 长这样:
| A(字符串标题) | B(日期标题) | C(数字标题) | |
|---|---|---|---|
| 1 | 字符串标题 | 日期标题 | 数字标题 |
| 2 | 字符串0 | 2026-09-29 10:00 | 0.56 |
| 3 | 字符串1 | 2026-09-29 10:00 | 1.56 |
| … | … | … | … |
4.2 Lambda 供给式写入(分批写,内存友好)
如果数据量特别大(比如几十万行),不要一次性构造 ArrayList,用 Lambda 按需供给:
1 | |
4.3 多 Sheet / 多次写入(ExcelWriter 手动控制)
需要写多个 Sheet 或者追加数据时,用 try-with-resources 管理 ExcelWriter:
1 | |
4.4 写入注解详解
| 注解 | 写入时作用 | 示例 |
|---|---|---|
@ExcelProperty("列名") | 指定表头名称和顺序 | @ExcelProperty("用户名") |
@ExcelProperty(index = 2) | 按列号固定写入位置 | @ExcelProperty(index = 0) 写 A 列 |
@ExcelIgnore | 该字段不写入 Excel | |
@ColumnWidth(20) | 设置列宽(字符数) | @ColumnWidth(30) |
@HeadRowHeight(30) | 设置表头行高 | |
@ContentRowHeight(20) | 设置数据行高 |
示例:
1 | |
5)生产环境最佳实践 & 避坑指南
5.1 大文件读取:分批入库的标准写法
监听器不要持有大量数据,每读一批就入库一次:
1 | |
5.2 避坑清单 ⚠️
| 坑点 | 说明 | 正确做法 |
|---|---|---|
| 监听器被 Spring 单例管理 | 多线程下同一个 List 会被多个请求同时写,数据串了 | 每次读取都 new 一个 Listener,不要加 @Component |
| invoke 里一条一条入库 | 10 万行数据就发 10 万条 SQL,数据库直接挂 | 攒够 BATCH_SIZE(300~1000)批量写 |
| doReadSync 读大文件 | 全量加载进内存,OOM 分分钟 | 数据 >1 万行一律用 doRead() + ReadListener |
| POI 版本冲突 | 项目里老版本 POI 跟 Fesod 依赖的版本不一致 | exclusions 排除旧 POI,统一用 Fesod 传递的版本 |
| 日期/数字格式化不生效 | 注解放错位置或者类型不匹配 | Date 用 @DateTimeFormat、BigDecimal/Number 用 @NumberFormat |
5.3 异常处理:某一行失败不中断整体
监听器里加 try-catch + 错误记录,不要让单条脏数据导致整个任务失败:
1 | |
6)从 EasyExcel / FastExcel 迁移到 Fesod
如果你之前用的是 EasyExcel 或旧版 FastExcel,迁移到 Fesod 几乎是 无缝的——API 设计基本保持一致,主要改动点:
| 旧写法(EasyExcel / FastExcel) | 新写法(Apache Fesod) |
|---|---|
EasyExcel.read(...) / FastExcel.read(...) | FesodSheet.read(...) |
EasyExcel.write(...) / FastExcel.write(...) | FesodSheet.write(...) |
EasyExcel.writerSheet(...) | FesodSheet.writerSheet(...) |
包名 com.alibaba.excel.* 或 cn.idev.excel.* | 目前仍然是 cn.idev.excel.*,孵化毕业后会逐步迁移到 org.apache.fesod.*,届时官方会出迁移指南 |
核心注解保持 100% 兼容:@ExcelProperty、@ExcelIgnore、@DateTimeFormat、@NumberFormat 都不需要改。
所以如果你手上有 EasyExcel 的老项目,试一下把入口类名替换成 FesodSheet,大概率直接跑起来,还能免费获得一波性能提升 👍。
7)总结 & 学习资源
7.1 一句话复盘
- 读 Excel:大文件用
FesodSheet.read().sheet().doRead()+ 自定义ReadListener,小文件用.doReadSync(); - 写 Excel:简单场景
FesodSheet.write().sheet().doWrite(list),多 Sheet / 大数据量用ExcelWriter+try-with-resources; - 生产环境:监听器别加 @Component、数据攒批入库、异常单独捕获记录行号。
7.2 官方学习资源
| 资源 | 链接 |
|---|---|
| 项目官网 | https://fesod.apache.org/ |
| 官方文档 | https://fesod.apache.org/docs/ |
| GitHub 源码(含大量 Examples) | https://github.com/apache/fesod |
| Maven 中央仓库 | https://mvnrepository.com/artifact/cn.idev.excel/fastexcel |
| 开发者邮件列表 | dev@fesod.apache.org |
7.3 后续进阶方向
掌握本篇基础后,可以进一步研究 Fesod 的高级能力:
- 模板填充(Fill API):用
.xlsx模板 + 数据变量,生成复杂格式的合同、报价单、财务报表; - 自定义 Converter:处理特殊字段类型(枚举、字典翻译、加密字段等);
- 单元格样式 / 合并单元格:动态生成带颜色、边框、公式的报表;
- Web 场景下的导入导出:配合 Spring Boot
MultipartFile和HttpServletResponse实现接口级 Excel 能力。
下一篇我会基于 Spring Boot 写一个 “生产级 Excel 导入导出通用组件”,从文件上传、参数校验、异步任务、错误报告下载全链路打通,敬请期待 🚀。