Easyexcel 使用实战

一、EasyExcel 简介

EasyExcel 是阿里巴巴开源的 Java Excel 处理工具,支持高效读写 Excel(.xls 和 .xlsx)。其最大特点是极低内存占用,适合大数据量场景,且 API 简洁易用。


二、核心原理

  • POI 封装:底层基于 Apache POI,但对大数据量做了优化(如 SAX 解析)。
  • 流式处理:写出时一行一行写,读取时一行一行读,避免全部加载到内存。
  • 注解驱动:通过注解映射 Java 对象和 Excel 列。

三、常见场景

  1. 写入 Excel
  2. 读取 Excel
  3. 多 Sheet 操作
  4. 复杂表头/合并单元格
  5. 自定义样式、公式、图片

四、核心 API 详解

1. 写入 Excel

方式一:简单写入
public class UserData {
    @ExcelProperty("姓名")
    private String name;
    @ExcelProperty("年龄")
    private Integer age;
    // getter/setter
}

List<UserData> list = ...;
EasyExcel.write("user.xlsx", UserData.class).sheet("用户信息").doWrite(list);
方式二:多 Sheet 写入
ExcelWriter writer = EasyExcel.write("multi.xlsx").build();
WriteSheet sheet1 = EasyExcel.writerSheet(0, "Sheet1").head(UserData.class).build();
WriteSheet sheet2 = EasyExcel.writerSheet(1, "Sheet2").head(UserData.class).build();
writer.write(data1, sheet1);
writer.write(data2, sheet2);
writer.finish();
方式三:自定义样式
HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headStyle, contentStyle);
EasyExcel.write("styled.xlsx", UserData.class)
    .registerWriteHandler(styleStrategy)
    .sheet("样式")
    .doWrite(list);

2. 读取 Excel

方式一:同步读(小数据量)
List<UserData> list = EasyExcel.read("user.xlsx").head(UserData.class).sheet().doReadSync();
方式二:监听器异步读(大数据量)
public class UserDataListener extends AnalysisEventListener<UserData> {
    @Override
    public void invoke(UserData data, AnalysisContext context) {
        // 每读一行回调一次
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 读完回调
    }
}

EasyExcel.read("user.xlsx", UserData.class, new UserDataListener()).sheet().doRead();

3. 复杂表头

public class ComplexHeadData {
    @ExcelProperty({"一级表头", "姓名"})
    private String name;
    @ExcelProperty({"一级表头", "年龄"})
    private Integer age;
}

4. 合并单元格

// 注册合并策略
EasyExcel.write("merge.xlsx", UserData.class)
    .registerWriteHandler(new LoopMergeStrategy(2, 0)) // 每两行合并第一列
    .sheet("合并")
    .doWrite(list);

五、实战技巧

  1. 大数据量读写
    推荐使用监听器模式,避免内存溢出。可以分批处理数据。
  2. 自定义转换器
    实现 Converter 接口,支持日期、枚举等特殊类型。
  3. 动态表头
    可通过 write 方法的 head 参数动态生成表头。
  4. 图片/公式写入
    使用 ImageWriteHandlerWriteCellData 等高级 API。

六、常见问题

  1. 内存溢出
    读写千万级数据时,采用分批处理和监听器模式。
  2. 多 Sheet 读写
    需手动构建 Sheet 并逐个写入/读取。
  3. 合并单元格不生效
    合并策略需注册在写操作中。
  4. 动态表头不对齐
    保证数据和表头顺序一致。

七、常用注解

  • @ExcelProperty:指定列名、支持多级表头
  • @ExcelIgnore:忽略字段
  • @ColumnWidth:设置列宽
  • @DateTimeFormat:日期格式
  • @NumberFormat:数字格式

八、总结

EasyExcel 是处理 Excel 的利器,适合大数据量、高性能场景。掌握注解映射、监听器模式、样式处理和合并策略,可以应对绝大多数 Excel 读写需求。

九、海量数据导入示例

1、场景说明

假设你有一个 Excel 文件,里面有上百万行数据,需要高效地导入数据库。常规的同步读取会导致内存溢出,推荐使用 EasyExcel 的监听器(AnalysisEventListener)+ 分批入库的方式。


2、准备工作

 引入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>

定义数据模型

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

@Data
public class UserData {
    @ExcelProperty("姓名")
    private String name;
    @ExcelProperty("年龄")
    private Integer age;
}

3、监听器实现(分批处理)

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;

public class UserDataListener extends AnalysisEventListener<UserData> {

    private static final int BATCH_COUNT = 1000; // 每1000条存一次
    private List<UserData> cachedDataList = new ArrayList<>(BATCH_COUNT);

    // 假设有数据库操作service
    private final UserService userService;

    public UserDataListener(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void invoke(UserData data, AnalysisContext context) {
        cachedDataList.add(data);
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            cachedDataList.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 处理剩余数据
        if (!cachedDataList.isEmpty()) {
            saveData();
        }
    }

    private void saveData() {
        // 批量插入数据库
        userService.saveBatch(cachedDataList);
    }
}

4、读取 Excel 文件

import com.alibaba.excel.EasyExcel;

public class ImportExcelDemo {
    public static void main(String[] args) {
        String fileName = "D:/data/huge_data.xlsx";
        UserService userService = new UserService(); // 你自己的数据库Service
        EasyExcel.read(fileName, UserData.class, new UserDataListener(userService)).sheet().doRead();
    }
}

5、补充说明

  1. 分批入库
    推荐每批 500~2000 条,避免单次插入过多造成数据库压力。
  2. 多线程处理
    EasyExcel 读取本身是单线程的,如需多线程入库,可在 saveData() 内异步处理或用线程池。
  3. 异常处理
    可以重写 onException 方法,记录或处理异常数据。
  4. 大文件优化
    • 尽量不要用同步读取(doReadSync),容易 OOM。
    • 只读需要的 Sheet,不要全表扫描。
    • 数据库批量插入要用批处理接口(如 MyBatis 的 batch、JPA 的 saveAll)。


6、完整流程总结

  1. 定义数据模型(对应 Excel 列)
  2. 实现监听器(分批缓存并批量入库)
  3. 调用 EasyExcel.read(监听器处理每一行)
  4. 优化批处理和异常处理

十、海量数据导出示例

1、场景说明

假设你需要将数据库中的百万级数据导出为 Excel 文件。一次性全部读出并写入容易造成内存溢出,推荐分批查询+分批写出,并使用 EasyExcel 的流式写入。


2、准备工作

引入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>

定义数据模型

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

@Data
public class UserData {
    @ExcelProperty("姓名")
    private String name;
    @ExcelProperty("年龄")
    private Integer age;
}

3、分批写出示例

 使用 ExcelWriter 分批写出

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;

public class ExportExcelDemo {

    public static void main(String[] args) {
        String fileName = "D:/data/huge_export.xlsx";
        ExcelWriter excelWriter = EasyExcel.write(fileName, UserData.class).build();
        WriteSheet writeSheet = EasyExcel.writerSheet("用户数据").build();

        int batchSize = 1000; // 每批写1000条
        int totalCount = getTotalCount(); // 总数据量
        int pageCount = (totalCount + batchSize - 1) / batchSize;

        for (int page = 0; page < pageCount; page++) {
            List<UserData> batchList = queryData(page, batchSize); // 从数据库查询一批
            excelWriter.write(batchList, writeSheet);
        }

        excelWriter.finish();
    }

    // 示例:获取总数据量
    private static int getTotalCount() {
        // 这里应查询数据库,例如 SELECT COUNT(*) FROM user
        return 1000000;
    }

    // 示例:分页查询数据
    private static List<UserData> queryData(int page, int batchSize) {
        // 这里应查询数据库,例如 LIMIT offset, batchSize
        // 这里只演示,实际需替换为你的数据查询逻辑
        List<UserData> list = new ArrayList<>();
        for (int i = 0; i < batchSize; i++) {
            UserData user = new UserData();
            user.setName("用户" + (page * batchSize + i));
            user.setAge(20 + (i % 30));
            list.add(user);
        }
        return list;
    }
}

 多 Sheet 分批写出(可选)

如果数据量过大,可以分 Sheet 导出:

ExcelWriter excelWriter = EasyExcel.write(fileName, UserData.class).build();
int sheetSize = 50000; // 每个Sheet 5万条
int totalCount = getTotalCount();
int sheetCount = (totalCount + sheetSize - 1) / sheetSize;

for (int sheetNum = 0; sheetNum < sheetCount; sheetNum++) {
    WriteSheet writeSheet = EasyExcel.writerSheet(sheetNum, "Sheet" + (sheetNum + 1)).build();
    for (int page = 0; page < sheetSize / batchSize; page++) {
        int offset = sheetNum * sheetSize + page * batchSize;
        List<UserData> batchList = queryDataByOffset(offset, batchSize);
        excelWriter.write(batchList, writeSheet);
    }
}
excelWriter.finish();

4、性能优化建议

  1. 分批查询数据库
    不要一次性查出所有数据,避免 OOM。
  2. ExcelWriter 流式写出
    用 build() 创建 writer,分批写入数据,最后 finish() 关闭资源。
  3. 多 Sheet 导出
    每个 Sheet 控制在 5~10 万行,便于 Excel 打开。
  4. 使用临时文件目录
    大文件导出时,建议写到磁盘临时目录,再提供下载。
  5. JVM 内存设置
    如需导出超大文件,适当提升 JVM 堆内存。

5、补充说明

  • 如果需要自定义样式、合并单元格、插入图片等,可注册相关 WriteHandler。
  • 若在 Web 项目中导出,可用 EasyExcel.write(outputStream),写到响应流。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猩火燎猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值