# 基于EasyExcel的通用基础转换器 **Repository Path**: IamHzc/easy-excel-convert ## Basic Information - **Project Name**: 基于EasyExcel的通用基础转换器 - **Description**: 基于EasyExcel的通用基础转换器 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-08-11 - **Last Updated**: 2026-03-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # easy-excel-convert 一个基于 **Alibaba EasyExcel** 的小工具库,主要解决两类问题: 1. **枚举 code 与描述之间的双向转换**(如 0/1、MALE/FEMALE ↔ 是/否、男/女) 2. **大数据量 Excel 导入导出批处理**(分页/分批处理,支持多线程并发、按 Sheet 拆分) --- ## 功能特性 - **通用枚举转换器**: - 支持任意实现 `BaseCodeDescEnum` 接口的枚举类型 - 支持任意 `Object` 类型的 code:如 `String`、`Integer`、`Long` 等 - 通过继承 `CommonEnumConverter` 即可快速得到一个可在 EasyExcel 中直接使用的转换器 - **大数据量批处理导入导出**: - `EasyExcelBatchHelper` 封装了按 **批次/分页** 导入导出逻辑,避免一次性加载全部数据引发 OOM - **导入:**基于 `PageReadListener`,按批回调处理函数 - **导出:**支持单线程分页导出 & 多线程并发分页导出,自动根据总量拆分多个 Sheet - **支持文件 / 流两种模式**,方便与 Web 场景集成 - 支持 **按字段过滤导出**(只导出指定字段) --- ## 项目结构 ```text src/ ├── main/java/com/easy/excel/convert/ │ ├── enums/ # 枚举定义 │ │ ├── BaseCodeDescEnum.java # 通用枚举接口(code/desc + 工具方法) │ │ ├── SexEnum.java # 性别枚举 │ │ └── YesOrNoEnum.java # 是否枚举 │ ├── CommonEnumConverter.java # 通用枚举转换器(基于 BaseCodeDescEnum) │ ├── SexConverter.java # 性别字段转换器示例 │ ├── YesOrNoConverter.java # 是否字段转换器示例 │ └── utils/ │ └── EasyExcelBatchHelper.java # 大数据量导入导出批处理工具 └── test/java/com/easy/excel/convert/ ├── ConvertTest.java # 枚举转换 + EasyExcel 示例 ├── EasyExcelBatchHelperTest.java # 批处理导入导出示例 └── TestData.java # 测试数据模型 ``` --- ## 环境 & 依赖 - **JDK**: 8+ - **构建工具**: Maven `pom.xml` 中的核心依赖: ```xml com.alibaba easyexcel 4.0.3 org.projectlombok lombok 1.18.32 com.alibaba transmittable-thread-local 2.14.5 ``` 如需引入到你的项目中,可将本模块打包并在业务项目中通过 Maven 依赖/本地 jar 的方式使用。 --- ## 一、枚举转换能力 ### 1. BaseCodeDescEnum 接口 所有需要支持 Excel 转换的枚举都必须实现此接口: ```java public interface BaseCodeDescEnum { /** 获取枚举 code 值 */ T getCode(); /** 获取枚举描述信息 */ String getDesc(); /** 根据 code 获取 desc */ static & BaseCodeDescEnum> String getDescByCode(Class enumClass, T code) { ... } /** 根据 desc 获取 code */ static & BaseCodeDescEnum> T getCodeByDesc(Class enumClass, String desc) { ... } } ``` ### 2. 定义枚举示例 ```java @Getter public enum SexEnum implements BaseCodeDescEnum { MALE("MALE", "男"), FEMALE("FEMALE", "女"); private final String code; private final String desc; SexEnum(String code, String desc) { this.code = code; this.desc = desc; } } ``` ```java @Getter public enum YesOrNoEnum implements BaseCodeDescEnum { NO(0, "否"), YES(1, "是"); private final Integer code; private final String desc; YesOrNoEnum(Integer code, String desc) { this.code = code; this.desc = desc; } } ``` ### 3. 创建对应转换器 只需要继承 `CommonEnumConverter` 即可: ```java public class SexConverter extends CommonEnumConverter { } public class YesOrNoConverter extends CommonEnumConverter { } ``` ### 4. 在实体类中使用 ```java @Data public class TestData { @ExcelProperty("ID") private Integer id; @ExcelProperty("姓名") private String name; @ExcelProperty(value = "是否有效", converter = YesOrNoConverter.class) private Integer valid; @ExcelProperty("年龄") private Integer age; @ExcelProperty(value = "性别", converter = SexConverter.class) private String sex; } ``` ### 5. 与 EasyExcel 结合导入导出 ```java // 导出 File file = new File("E:/test_export.xlsx"); EasyExcel.write(file, TestData.class) .sheet("测试数据") .doWrite(dataList); // 导入 List readList = EasyExcel.read(file) .head(TestData.class) .sheet() .doReadSync(); ``` 枚举字段会在导出时自动将 code 转为中文描述,在导入时再根据描述还原为 code。 --- ## 二、大数据量批处理工具 EasyExcelBatchHelper `EasyExcelBatchHelper` 提供一系列静态方法,简化大数据量导入导出的代码,并控制内存使用。 ### 1. 按批导入(文件 / 流) **方式一:传入 `File`** ```java File file = new File("D:/test/test.xlsx"); EasyExcelBatchHelper.importByBatch( file, TestData.class, 1000, // 每批 1000 条 batch -> { // 这里处理每一批数据,例如入库 } ); ``` **方式二:传入 `InputStream`** ```java try (InputStream in = new FileInputStream(file)) { EasyExcelBatchHelper.importByBatch( in, TestData.class, 1000, batch -> { // 处理每一批 } ); } ``` > 每次只会把一批数据加载到内存,处理完立刻释放,适合大文件场景。 ### 2. 单线程按批导出 **文件方式:** ```java int totalCount = ...; // 总记录数 int batchSize = 10000; // 每批 1 万 File file = new File("D:/simple_export_test.xlsx"); EasyExcelBatchHelper.exportByBatch( file, TestData.class, totalCount, batchSize, (offset, limit) -> { // 根据 offset/limit 从数据库分页查询 return queryFromDb(offset, limit); }, batch -> { // 写入前对当前批次的处理(可为 null) } ); ``` **流方式:** ```java try (OutputStream out = response.getOutputStream()) { EasyExcelBatchHelper.exportByBatch( out, TestData.class, totalCount, 5000, (offset, limit) -> queryFromDb(offset, limit), null ); } ``` 当 `totalCount <= 0` 时,工具会自动只写入表头,不抛异常。 ### 3. 多线程按批导出(并发分页查询) 当单次查询耗时较长(例如每页都访问远程服务/复杂 SQL)时,可以使用多线程版本: ```java int totalCount = ...; int batchSize = 10000; File file = new File("D:/thread_export_test.xlsx"); EasyExcelBatchHelper.exportByBatchParallel( file, TestData.class, totalCount, batchSize, null, // 条件对象,可自定义 (page, limit, condition) -> { // page 从 0 开始或 1 开始由业务自行约定,本工具负责按页顺序写入 return queryPage(page, limit, condition); }, null, // 写入前处理逻辑 4, // 线程池大小 8, // 最大并发查询数 null // 需要导出的字段集合(null 表示全部) ); ``` 内部通过: - `ThreadPoolExecutor` + `Semaphore` 控制最大并发查询数 - `TtlExecutors` 确保线程上下文(如 ThreadLocal)可传递 - 使用有序写入机制,保证最终 Excel 中的顺序与单线程导出一致 ### 4. 按字段过滤导出 所有导出方法都有带 `includeFields` 的重载,可只导出部分字段: ```java Collection includeFields = Arrays.asList("id", "name", "sex"); EasyExcelBatchHelper.exportByBatch( file, TestData.class, totalCount, batchSize, (offset, limit) -> queryFromDb(offset, limit), null, includeFields ); ``` 字段名需与实体类中的属性名一致。 --- ## 三、如何运行示例测试 在工程根目录执行: ```bash mvn test ``` 主要示例: - `ConvertTest`:演示使用 `YesOrNoConverter`、`SexConverter` 进行简单的导入导出与日志打印 - `EasyExcelBatchHelperTest`:演示批量导入、单线程/多线程批量导出、流式导入导出等场景 --- ## 四、扩展指引 如果你要在自己的项目中新增一个需要 Excel code/描述转换的枚举: 1. **创建枚举类**,实现 `BaseCodeDescEnum<你的 code 类型>` 接口 2. **创建转换器类**,继承 `CommonEnumConverter<你的 code 类型, 你的枚举>` 3. **在实体类字段上** 使用 `@ExcelProperty(value = "列名", converter = 你的转换器.class)` 即可 如果你要对大数据量做导出/导入: - 导入:优先使用 `EasyExcelBatchHelper.importByBatch`,把入库/业务逻辑写在批处理回调里 - 导出:根据数据量和查询耗时选择 `exportByBatch`(单线程)或 `exportByBatchParallel`(多线程) 如需对 README 增加更多示例或英文说明,可以在此基础上继续补充。