# 基于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 增加更多示例或英文说明,可以在此基础上继续补充。