迁移指南:从 FastExcel 迁移到 Apache Fesod (Incubating)
概述
本指南为从 cn.idev FastExcel 迁移到 Apache Fesod (Incubating) 提供全面的迁移路线。Apache Fesod (Incubating) 是该项目的演进版本,现已成为 Apache 软件基金会(孵化中)的一部分,提供同样的高性能 Excel 处理能力,并具有更强的社区支持和长期可持续性。
为什么要迁移?
- Apache 软件基金会支持:Apache Fesod (Incubating) 现已成为 Apache 软件基金会的一部分,确保长期维护和社区驱动的开发
- 无缝过渡:API 几乎完全相同,只需最少的代码更改
- 统一品牌:在 Apache Fesod (Incubating) 下统一的命名规范
- 持续创新:在 Apache 治理下获得未来的增强和功能
- 向后兼容:暂时保留已废弃的别名(FastExcel)以便逐步迁移
迁移范围
本次迁移主要涉及:
- 更新 Maven/Gradle 依赖
- 将已废弃的类名替换为 FesodSheet
- 更新包导入
- 通过全面测试验证功能
核心 API、注解和处理逻辑保持不变,确保低风险的迁移路径。
迁移步骤
步骤 1:更新依赖
将现有依赖替换为 Apache Fesod (Incubating):
| 来源 | GroupId | ArtifactId | 版本 |
|---|---|---|---|
| cn.idev FastExcel | cn.idev.excel | fastexcel | 1.3.0 |
| Apache Fesod (Incubating) ✅ | org.apache.fesod | fesod-sheet | 2.0.2-incubating+ |
Maven 配置:
修改前:
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>1.3.0</version>
</dependency>
修改后:
<dependency>
<groupId>org.apache.fesod</groupId>
<artifactId>fesod-sheet</artifactId>
<version>2.0.2-incubating</version>
</dependency>
Gradle 配置:
修改前:
implementation 'cn.idev.excel:fastexcel:1.3.0'
修改后:
implementation 'org.apache.fesod:fesod-sheet:2.0.2-incubating'
注意:
fesod-sheet模块是 Excel/CSV 处理的核心模块。它会自动引入必要的依赖(fesod-common和fesod-shaded)。
步骤 2:更新包导入
更新所有 import 语句以使用新的 Apache Fesod (Incubating) 包结构。
核心入口类替换:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel.FastExcel; | import org.apache.fesod.sheet.FastExcel; |
import cn.idev.excel.FastExcelFactory; | import org.apache.fesod.sheet.FastExcelFactory; |
import cn.idev.excel.ExcelWriter; | import org.apache.fesod.sheet.ExcelWriter; |
import cn.idev.excel.ExcelReader; | import org.apache.fesod.sheet.ExcelReader; |
注意: 在此阶段之后,
FastExcel.read(...)和FastExcelFactory.writerSheet(...)依然可以编译通过,它们会解析到 Fesod 中带有@Deprecated标记的桥接类。 在 “步骤 3” 中我们会将它们彻底替换为规范的 FesodSheet 类。
读取 API 替换:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel.read.listener.ReadListener; | import org.apache.fesod.sheet.read.listener.ReadListener; |
import cn.idev.excel.read.listener.PageReadListener; | import org.apache.fesod.sheet.read.listener.PageReadListener; |
import cn.idev.excel.read.metadata.ReadSheet; | import org.apache.fesod.sheet.read.metadata.ReadSheet; |
import cn.idev.excel.read.metadata.ReadWorkbook; | import org.apache.fesod.sheet.read.metadata.ReadWorkbook; |
import cn.idev.excel.read.metadata.ReadBasicParameter; | import org.apache.fesod.sheet.read.metadata.ReadBasicParameter; |
import cn.idev.excel.read.builder.ExcelReaderBuilder; | import org.apache.fesod.sheet.read.builder.ExcelReaderBuilder; |
import cn.idev.excel.read.builder.ExcelReaderSheetBuilder; | import org.apache.fesod.sheet.read.builder.ExcelReaderSheetBuilder; |
import cn.idev.excel.context.AnalysisContext; | import org.apache.fesod.sheet.context.AnalysisContext; |
import cn.idev.excel.event.SyncReadListener; | import org.apache.fesod.sheet.event.SyncReadListener; |
写入 API 替换:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel.write.metadata.WriteSheet; | import org.apache.fesod.sheet.write.metadata.WriteSheet; |
import cn.idev.excel.write.metadata.WriteWorkbook; | import org.apache.fesod.sheet.write.metadata.WriteWorkbook; |
import cn.idev.excel.write.metadata.WriteTable; | import org.apache.fesod.sheet.write.metadata.WriteTable; |
import cn.idev.excel.write.metadata.WriteBasicParameter; | import org.apache.fesod.sheet.write.metadata.WriteBasicParameter; |
import cn.idev.excel.write.builder.ExcelWriterBuilder; | import org.apache.fesod.sheet.write.builder.ExcelWriterBuilder; |
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder; | import org.apache.fesod.sheet.write.builder.ExcelWriterSheetBuilder; |
import cn.idev.excel.write.builder.ExcelWriterTableBuilder; | import org.apache.fesod.sheet.write.builder.ExcelWriterTableBuilder; |
写入拦截器替换:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel.write.handler.WriteHandler; | import org.apache.fesod.sheet.write.handler.WriteHandler; |
import cn.idev.excel.write.handler.SheetWriteHandler; | import org.apache.fesod.sheet.write.handler.SheetWriteHandler; |
import cn.idev.excel.write.handler.CellWriteHandler; | import org.apache.fesod.sheet.write.handler.CellWriteHandler; |
import cn.idev.excel.write.handler.RowWriteHandler; | import org.apache.fesod.sheet.write.handler.RowWriteHandler; |
import cn.idev.excel.write.handler.WorkbookWriteHandler; | import org.apache.fesod.sheet.write.handler.WorkbookWriteHandler; |
import cn.idev.excel.write.handler.context.CellWriteHandlerContext; | import org.apache.fesod.sheet.write.handler.context.CellWriteHandlerContext; |
注解替换:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel.annotation.ExcelProperty; | import org.apache.fesod.sheet.annotation.ExcelProperty; |
import cn.idev.excel.annotation.ExcelIgnore; | import org.apache.fesod.sheet.annotation.ExcelIgnore; |
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated; |
import cn.idev.excel.annotation.format.DateTimeFormat; | import org.apache.fesod.sheet.annotation.format.DateTimeFormat; |
import cn.idev.excel.annotation.format.NumberFormat; | import org.apache.fesod.sheet.annotation.format.NumberFormat; |
import cn.idev.excel.annotation.write.style.ColumnWidth; | import org.apache.fesod.sheet.annotation.write.style.ColumnWidth; |
import cn.idev.excel.annotation.write.style.HeadStyle; | import org.apache.fesod.sheet.annotation.write.style.HeadStyle; |
import cn.idev.excel.annotation.write.style.ContentStyle; | import org.apache.fesod.sheet.annotation.write.style.ContentStyle; |
import cn.idev.excel.annotation.write.style.HeadFontStyle; | import org.apache.fesod.sheet.annotation.write.style.HeadFontStyle; |
import cn.idev.excel.annotation.write.style.ContentFontStyle; | import org.apache.fesod.sheet.annotation.write.style.ContentFontStyle; |
import cn.idev.excel.annotation.write.style.HeadRowHeight; | import org.apache.fesod.sheet.annotation.write.style.HeadRowHeight; |
import cn.idev.excel.annotation.write.style.ContentRowHeight; | import org.apache.fesod.sheet.annotation.write.style.ContentRowHeight; |
import cn.idev.excel.annotation.write.style.OnceAbsoluteMerge; | import org.apache.fesod.sheet.annotation.write.style.OnceAbsoluteMerge; |
import cn.idev.excel.annotation.write.style.ContentLoopMerge; | import org.apache.fesod.sheet.annotation.write.style.ContentLoopMerge; |
转换器替换:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel.converters.Converter; | import org.apache.fesod.sheet.converters.Converter; |
import cn.idev.excel.converters.AutoConverter; | import org.apache.fesod.sheet.converters.AutoConverter; |
import cn.idev.excel.converters.ReadConverterContext; | import org.apache.fesod.sheet.converters.ReadConverterContext; |
import cn.idev.excel.converters.WriteConverterContext; | import org.apache.fesod.sheet.converters.WriteConverterContext; |
枚举替换:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel.enums.CellDataTypeEnum; | import org.apache.fesod.sheet.enums.CellDataTypeEnum; |
import cn.idev.excel.enums.CellExtraTypeEnum; | import org.apache.fesod.sheet.enums.CellExtraTypeEnum; |
import cn.idev.excel.enums.WriteDirectionEnum; | import org.apache.fesod.sheet.enums.WriteDirectionEnum; |
import cn.idev.excel.enums.poi.HorizontalAlignmentEnum; | import org.apache.fesod.sheet.enums.poi.HorizontalAlignmentEnum; |
import cn.idev.excel.enums.poi.BorderStyleEnum; | import org.apache.fesod.sheet.enums.poi.BorderStyleEnum; |
import cn.idev.excel.enums.poi.FillPatternTypeEnum; | import org.apache.fesod.sheet.enums.poi.FillPatternTypeEnum; |
异常与元信息替换:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel.exception.ExcelAnalysisException; | import org.apache.fesod.sheet.exception.ExcelAnalysisException; |
import cn.idev.excel.exception.ExcelAnalysisStopException; | import org.apache.fesod.sheet.exception.ExcelAnalysisStopException; |
import cn.idev.excel.exception.ExcelCommonException; | import org.apache.fesod.sheet.exception.ExcelCommonException; |
import cn.idev.excel.exception.ExcelGenerateException; | import org.apache.fesod.sheet.exception.ExcelGenerateException; |
import cn.idev.excel.metadata.data.WriteCellData; | import org.apache.fesod.sheet.metadata.data.WriteCellData; |
import cn.idev.excel.metadata.data.ReadCellData; | import org.apache.fesod.sheet.metadata.data.ReadCellData; |
import cn.idev.excel.metadata.CellExtra; | import org.apache.fesod.sheet.metadata.CellExtra; |
import cn.idev.excel.metadata.Head; | import org.apache.fesod.sheet.metadata.Head; |
import cn.idev.excel.metadata.property.ExcelContentProperty; | import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; |
通配符兜底替换
在完成上述所有具体替换后,扫描任何剩余的通配符包导入:
| 修改前 | 修改后 |
|---|---|
import cn.idev.excel. | import org.apache.fesod.sheet. |
import org.apache.fesod.excel. | import org.apache.fesod.sheet. |
注意:通配符规则不适用于
cn.idev.excel.util.*的导入。这些工具类迁移到了fesod-common模块,而不是org.apache.fesod.sheet—— 请改为更新为org.apache.fesod.common.util(参见步骤 4)。
步骤 3:重命名入口类(强烈建议)
FastExcel 和 FastExcelFactory 在 Fesod 中仍然可以编译,但已被标记为 @Deprecated 弃用,
并且将在未来的版本中被移除。请将所有调用点替换为 FesodSheet。
导入替换:
| 修改前 | 修改后 |
|---|---|
import org.apache.fesod.sheet.FastExcel; | import org.apache.fesod.sheet.FesodSheet; |
import org.apache.fesod.sheet.FastExcelFactory; | import org.apache.fesod.sheet.FesodSheet; |
调用入口类替换 - FastExcel
| 修改前 | 修改后 |
|---|---|
FastExcel.read() | FesodSheet.read() |
FastExcel.write() | FesodSheet.write() |
FastExcel.writerSheet() | FesodSheet.writerSheet() |
FastExcel.readSheet() | FesodSheet.readSheet() |
FastExcel.writerTable() | FesodSheet.writerTable() |
调用入口类替换 — FastExcelFactory
FastExcel 1.3 曾提供了 FastExcelFactory 作为第二个入口类,其 API 表面完全相同。它的所有静态方法都可以直接映射到 FesodSheet:
| 修改前 | 修改后 |
|---|---|
FastExcelFactory.read() | FesodSheet.read() |
FastExcelFactory.write() | FesodSheet.write() |
FastExcelFactory.writerSheet() | FesodSheet.writerSheet() |
FastExcelFactory.readSheet() | FesodSheet.readSheet() |
FastExcelFactory.writerTable() | FesodSheet.writerTable() |
类型引用重命名
如果 FastExcel 或 FastExcelFactory 作为类型名称(而不是调用入口)出现,也请重命名它们:
- 变量类型:
FastExcel x = ...→FesodSheet x = ... - 类字面量:
FastExcel.class→FesodSheet.class
ExcelWriter 和 ExcelReader 无需重命名,保持相同的类名即可。
CGLIB 类名替换(按需执行)
此阶段仅适用于项目中包含在运行时检查或断言生成的 CGLIB 类名的代码,例如在单元测试或特定的序列化逻辑中。 搜索所有 .java 文件中的字符串 ByFastExcelCGLIB。
如果找到,请替换为 ByFesodCGLIB。 在 Fesod 中,命名策略定义在 org.apache.fesod.sheet.util.BeanMapUtils.FesodSheetNamingPolicy 中,其 getTag()
返回 ByFesodCGLIB。 如果没有文件引用 ByFastExcelCGLIB,请直接跳过此步骤。
步骤 4:类名与模块变更(升级时按需检查)
除了入口类之外,捐赠过程中部分辅助类被重命名或迁移到了其他模块,且 Fesod 还包含一些 FastExcel 1.3 没有的功能。请在代码库中搜索以下名称,仅更新你实际引用到的部分。
已重命名的辅助类
| 修改前(FastExcel 1.3.0) | 修改后(Fesod) |
|---|---|
cn.idev.excel.constant.FastExcelConstants | org.apache.fesod.sheet.constant.FesodSheetConstants |
cn.idev.excel.util.FastExcelTempFileCreationStrategy | org.apache.fesod.sheet.util.FesodTempFileCreationStrategy |
cn.idev.excel.util.BeanMapUtils.FastExcelNamingPolicy | org.apache.fesod.sheet.util.BeanMapUtils.FesodSheetNamingPolicy |
迁移到 fesod-common 的工具类
以下工具类已从 cn.idev.excel.util 迁移至 org.apache.fesod.common.util。如果你的代码直接引用了它们(例如在自定义转换器或策略中),请更新导入路径。如果你在步骤 2 中执行过通配符替换,请复查这些导入没有被误改为 org.apache.fesod.sheet.util:
| 类 | 修改前 | 修改后 |
|---|---|---|
StringUtils | cn.idev.excel.util | org.apache.fesod.common.util |
IoUtils | cn.idev.excel.util | org.apache.fesod.common.util |
MapUtils | cn.idev.excel.util | org.apache.fesod.common.util |
ListUtils | cn.idev.excel.util | org.apache.fesod.common.util |
BooleanUtils | cn.idev.excel.util | org.apache.fesod.common.util |
IntUtils | cn.idev.excel.util | org.apache.fesod.common.util |
MemberUtils | cn.idev.excel.util | org.apache.fesod.common.util |
PositionUtils | cn.idev.excel.util | org.apache.fesod.common.util |
Fesod 新增功能
以下功能在 FastExcel 1.3.0 中不存在。迁移时值得了解,但除非你想使用它们,否则无需任何操作:
@FreezePane(org.apache.fesod.sheet.annotation.write.style.FreezePane):类级注解,写入工作表时冻结行和/或列(colSplit、rowSplit、leftmostColumn、topRow)。- URL 图片安全(
org.apache.fesod.sheet.converters.url):UrlImageFetchPolicy(由SchemePolicy和CidrBlock构建)为内置的 URL 图片转换器提供 SSRF 防护。默认只允许http/https与公网地址;可通过白名单放行私网、主机和 CIDR,并可限制重定向次数(maxRedirects)与图片大小(maxImageBytes)。 - 字符串图片转换器(
org.apache.fesod.sheet.converters.string):StringBase64ImageConverter(base64 值,可选data:前缀)与StringPathnameImageConverter(本地文件路径)将字符串转图片的转换显式化,兼容的StringImageConverter仍然保留。 HeadBuilder/DefaultHeadBuilder(org.apache.fesod.sheet.metadata):无 Bean 模式下的流式表头构建,例如FesodSheet.write(pathname).head(builder -> builder.column("ID", 2).columns("User Info", sub -> sub.column("Name").column("Age")))。HeaderMergeStrategy(org.apache.fesod.sheet.enums.HeaderMergeStrategy):显式控制表头合并——NONE、HORIZONTAL_ONLY、VERTICAL_ONLY、FULL_RECTANGLE、AUTO——通过写构建器上的headerMergeStrategy(...)设置,取代布尔开关automaticMergeHead。
迁移策略
渐进式迁移(推荐)
利用已废弃的别名类进行分阶段迁移。
阶段 1:仅更新依赖
- 将 Maven/Gradle 依赖更新为 Apache Fesod (Incubating)
- 继续使用 FastExcel 类(现为已废弃的别名)
- 仅更新包导入
- 运行全面测试以验证兼容性
阶段 2:类名迁移
- 逐步将已废弃的类替换为 FesodSheet
- 使用 IDE 重构工具进行批量重命名
- 逐个模块或逐个功能进行迁移
- 在整个过程中保持全面的测试覆盖
阶段 3:清理
- 删除所有对已废弃类的引用
- 解决废弃警告
- 更新文档和代码注释
优势:
- 通过增量更改降低风险
- 如果出现问题更容易回滚
- 对正在进行的开发影响最小
- 允许在每个阶段进行充分测试的时间
总结
由于高度的 API 兼容性和向后兼容的已废弃别名,从 cn.idev FastExcel 迁移到 Apache Fesod (Incubating) 是一个直接的过程。主要工作涉及更新依赖声明和包导入,几乎不需要或不需要逻辑更改。
渐进式迁移策略得到临时已废弃别名(FastExcel、FastExcelFactory)的支持,允许团队按自己的节奏迁移,同时保持完整功能。
遵循本指南,组织可以无缝过渡到 Apache Fesod (Incubating),并从 Apache 软件基金会生态系统的长期可持续性和社区支持中受益。