掘金 后端 ( ) • 2024-04-16 11:33

1.简介

在日常的开发工作中,Excel 文件的读取和写入是非常常见的需求,特别是在后台管理系统中更为频繁,基于传统的方式操作excel通常比较繁琐,EasyExcel 库的出现为我们带来了更简单、更高效的解决方案。本文将介绍 EasyExcel 库的基本用法和一些常见应用场景,帮助开发者更好地利用 EasyExcel 提高工作效率。官网地址:https://github.com/alibaba/easyexcel

青出于蓝而胜于蓝

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。 easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便

16M内存23秒读取75M(46W行25列)的Excel(3.2.1+版本)

当然还有极速模式能更快,但是内存占用会在100M多一点 !

EasyExcel 的核心类主要包括以下几个:

  1. ExcelReader: 用于读取 Excel 文件的核心类。通过 ExcelReader 类可以读取 Excel 文件中的数据,并进行相应的处理和操作。
  2. ExcelWriter: 用于写入 Excel 文件的核心类。通过 ExcelWriter 类可以将数据写入到 Excel 文件中,并进行样式设置、标题添加等操作。
  3. AnalysisEventListener: 事件监听器接口,用于处理 Excel 文件读取过程中的事件,如读取到每一行数据时的操作。
  4. AnalysisContext: 读取 Excel 文件时的上下文信息,包括当前行号、sheet 名称等。通过 AnalysisContext 可以获取到读取过程中的一些关键信息。
  5. WriteHandler: 写入 Excel 文件时的处理器接口,用于处理 Excel 文件的样式设置、标题添加等操作。
  6. WriteSheet: 写入 Excel 文件时的 Sheet 配置类,用于指定写入数据的 Sheet 名称、样式等信息。

这些核心类在 EasyExcel 中承担了不同的角色,协作完成了 Excel 文件的读取和写入操作。开发者可以根据具体的需求和场景,使用这些类来实现 Excel 文件的各种操作。

Alibaba EasyExcel的核心入口类是EasyExcel类,就想我们平时封装的Util类一样,通过它对excel数据读取或者导出。

2.EasyExcel数据导入

2.1.简单导入

准备excel数据文件

这里以用户信息数据为例

定义用户信息User类

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
​
    private Long id;
​
    private String userNo;
​
    private String name;
​
    private Integer gender;
​
    private Date birthday;
​
    private String phone;
​
    private String email;
​
    @ExcelIgnore
    private Integer isDelete;
​
    private String address;
​
​
}

事件监听器ReadListener

我们一般使用EasyExcelAnalysisEventListener,用于监听 Excel 文件读取事件的接口,通过实现这个接口,可以在读取 Excel 文件的过程中对数据进行处理和操作:

​
/**
 * @author fjzheng
 * @version 1.0
 * @date 2024/4/12 16:22
 */
@Slf4j
public class UserExcelListener extends AnalysisEventListener<User> {
​
    /**
     * 每隔100条处理下,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
​
​
    /**
     * 缓存的数据
     */
    private List<User> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
​
​
    /**
     *
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.error("======>>>解析异常:", exception);
        throw exception;
    }
​
    /**
     * 当读取到一行数据时,会调用这个方法,并将读取到的数据以及上下文信息作为参数传入
     * 可以在这个方法中对读取到的数据进行处理和操作,处理数据时要注意异常错误,保证读取数据的稳定性
     * @param user
     * @param context
     */
    @Override
    public void invoke(User user, AnalysisContext context) {
        log.info("解析到一条数据:{}", user);
        cachedDataList.add(user);
        if (cachedDataList.size() >= BATCH_COUNT) {
            // 处理缓存的数据,比如说入库。。。
            // 然后清空
            cachedDataList.clear();
        }
    }
​
    /**
     * 当每个sheet所有数据读取完毕后,会调用这个方法,可以在这个方法中进行一些收尾工作,如资源释放、数据汇总等。
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 收尾工作,处理剩下的缓存数据。。。
​
        log.info("sheet={} 所有数据解析完成!", context.readSheetHolder().getSheetName());
    }
​
}
​

注意 UserExcelListener 不能被Spring管理,要每次读取excel都要new一个新的监听器,如果里面用到spring bean,可以通过构造方法传进去

执行测试用例

​
/**
 * @author fjzheng
 * @version 1.0
 * @date 2024/4/11 18:04
 */
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ExcelTest {
​
    @Test
    public void testExcelRead() {
        String fileName = "/Users/shepherdmy/Desktop/test1.xlsx";
        EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet().doRead();
        // 如果excel是多行表头比如说2行,需要设置行头数headRowNumber,默认不设置为1行表头,sheet不传默认读取第一个sheet
//        EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet().headRowNumber(2).doRead();
    }
}

执行结果控制台输出如下:

解析到一条数据:User(id=1, userNo=she001, name=王小二, gender=0, birthday=Fri Apr 12 10:00:00 CST 2024, phone=123456789, [email protected], isDelete=null, address=杭州市余杭区未来科技城)
解析到一条数据:User(id=2, userNo=she002, name=张三, gender=1, birthday=Wed Apr 10 18:00:00 CST 2024, phone=987654321, [email protected], isDelete=null, address=杭州市拱墅区城西银泰)
解析到一条数据:User(id=3, userNo=she003, name=李四, gender=1, birthday=Thu Apr 11 18:00:00 CST 2024, phone=345686789, [email protected], isDelete=null, address=上海陆家嘴浦东)
sheet=用户信息 所有数据解析完成!

完美解析导入,你是否注意到上面我们定义User类的属性和excel文件的字段一一对应,顺序高度一致,如果顺序映射不一致,比如说定义的时候把姓名name和性别gender的字段顺序调换,会导致导入解析字符串类型转整数失败,这是因为在 EasyExcel 中,当导入数据时,默认情况下是按照 Excel 表格中列的顺序来映射数据的。也就是说,如果 Excel 表格的第一列对应 Java 对象的第一个属性,第二列对应第二个属性,以此类推。

在源码层面,EasyExcel 是通过反射机制来实现数据的映射的。具体来说,EasyExcel 在读取 Excel 文件时,会根据 Java 对象的属性顺序和 Excel 表格中列的顺序来一一对应。这个过程是在 EasyExcel 内部进行的,开发者不需要关心具体的实现细节,只需要提供正确的 Java 对象结构和 Excel 表格格式即可。

项目推荐:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装,解决业务开发时常见的非功能性需求,防止重复造轮子,方便业务快速开发和企业技术栈框架统一管理。引入组件化的思想实现高内聚低耦合并且高度可配置化,做到可插拔。严格控制包依赖和统一版本管理,做到最少化依赖。注重代码规范和注释,非常适合个人学习和企业使用

Github地址https://github.com/plasticene/plasticene-boot-starter-parent

Gitee地址https://gitee.com/plasticene3/plasticene-boot-starter-parent

微信公众号Shepherd进阶笔记

交流探讨qun:Shepherd_126

2.2 EasyExcel提供的注解

上面User类的定义使用了注解@ExcelIgnore,表示字段isDelete不参与数据映射,要不然严格按照顺序就会把excel的地址列映射给isDelete导致字符串转整数类型转换错误了。

EasyExcel 提供了一些注解,用于帮助开发者更灵活地控制 Excel 文件的读取和写入操作。以下是 EasyExcel 中常用的注解:

@ExcelProperty

@ExcelProperty 注解用于标注 Java 对象中的属性与 Excel 表格中的列的对应关系。该注解包含以下属性:

  • value:指定 Excel 表格中的列索引,表示该属性对应的列在 Excel 表格中的位置。支持字符串形式(如 "A"、"B"、"C")和数字形式(从 0 开始)。
  • index:同 value,用于指定 Excel 表格中的列索引。
  • indexvalue 二选一即可,用于指定 Excel 表格中的列索引。

示例:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "tb_user")
public class User {
​
    @TableId(type = IdType.AUTO)
    @ExcelProperty(index = 0)
    private Long id;
    @ExcelProperty(index = 1)
    private String userNo;
    @ExcelProperty(index = 3)
//    @ExcelProperty("性别")
    private Integer gender;
    @ExcelProperty(index = 2)
//    @ExcelProperty("姓名")
    private String name;
    @ExcelProperty(index = 4)
    private Date birthday;
    @ExcelProperty(index = 5)
    private String phone;
    @ExcelProperty(index = 6)
    private String email;
//    @ExcelIgnore
    private Integer isDelete;
    @ExcelProperty(index = 7)
    private String address;
}

一开始我们定义User类时并没有使用@ExcelProperty(),但其实等价于按照类属性顺序从0开始加上了@ExcelProperty(index =列顺序号),注意我特意把字段属性姓名name和性别gender调换了顺序,但是index是按照excel来写的,同时我去掉了isDelete的忽略属性@ExcelIgnore。正常解析导入。。。

在我们日常开发过程中,我个人建议在excel映射的类上必须使用@ExcelProperty()注解标注,至于使用@ExcelProperty(index = 2)还是@ExcelProperty("姓名")都可以,但不能同时用,我个人倾向于@ExcelProperty("姓名"),这样可以备注字段含义,当然一般人不太会修改excel表头的字段,但是很有可能因为显示查看会拖动excel列的顺序这样就会导致@ExcelProperty(index =列顺序号)映射出错。

@ExcelIgnore

@ExcelIgnore 注解用于标注 Java 对象中的属性,表示在 Excel 文件的读取和写入过程中忽略该属性。示例如上描述

@ExcelIgnoreUnannotated

@ExcelIgnoreUnannotated 注解用于指定在读取 Excel 文件时是否忽略未标注 @ExcelProperty 注解的属性,默认为 false,即不忽略。上面我们去掉了isDelete的@ExcelIgnore能正常导出那是因为我们excel文件只有8列刚刚好够标注了@ExcelProperty映射,加入我们excel文件在添加一列,就会去映射isDelete,不符合我们预期,这时候我们就需要在类使用 @ExcelIgnoreUnannotated解决此问题

@ExcelSheet

@ExcelSheet 注解用于指定读取 Excel 文件时的 Sheet 名称。通常用于读取多个 Sheet 的情况。

@ExcelIgnoreRowNum

@ExcelIgnoreRowNum 注解用于标注 Java 对象中的属性,表示在 Excel 文件的读取过程中忽略行号。通常用于读取时不需要关注行号的情况。

@ColumnWidth

@ColumnWidth主要是控制列宽

2.1.3 日期、数字或者自定义格式转换

数据转换我们只需要实现easyexcel封装的Converter接口即可,比如在上面的用户信息类User的学号属性userNo统一在excel的值加上前缀uno:, 性别属性gender由字符串转枚举值男:0 女:1 未知:2

UserNoConverter

public class UserNoConverter implements Converter<String> {
​
    /**
     * 支持的java类型
     * @return
     */
    @Override
    public Class<?> supportJavaTypeKey() {
        return String.class;
    }
​
    /**
     * 支持的excel值类型
     * @return
     */
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
​
    /**
     * 这里读的时候会调用
     *
     * @param context
     * @return
     */
    @Override
    public String convertToJavaData(ReadConverterContext<?> context) {
        return "uno:" + context.getReadCellData().getStringValue();
    }
​
    /**
     * 这里是写的时候会调用 不用管
     *
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
        return new WriteCellData<>(context.getValue());
    }
}
​

GenderConverter

public class GenderConverter implements Converter<Integer> {
​
    @Override
    public Class<?> supportJavaTypeKey() {
        return Integer.class;
    }
​
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
​
    /**
     * 从excel读数据时候调用
     * @param cellData
     * @param contentProperty
     * @param globalConfiguration
     * @return
     */
    @Override
    public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
                                     GlobalConfiguration globalConfiguration) {
        String value = cellData.getStringValue();
        if (StringUtils.isBlank(value)){
            // 未知
            return 2;
        }
        if (value.indexOf('男') != -1) {
            return 0;
        }
        if (value.indexOf('女') != -1) {
            return 1;
        }
        return 2;
    }
​
    /**
     * 写数据到excel里面
     * @param context
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) {
        Integer value = context.getValue();
        if (Objects.equals(value, 0)) {
           return new WriteCellData<>("男");
        }
        if (Objects.equals(value, 1)) {
            return new WriteCellData<>("女");
        }
        return new WriteCellData<>("未知");
    }
}
​

在映射的实体类User进行转换器绑定:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
// @ExcelIgnoreUnannotated
public class User {
​
    @ExcelProperty(index = 0)
    private Long id;
    @ExcelProperty(index = 1, converter = UserNoConverter.class)
    private String userNo;
    @ExcelProperty(index = 3, converter = GenderConverter.class)
//    @ExcelProperty("性别")
    private Integer gender;
    @ExcelProperty(index = 2)
//    @ExcelProperty("姓名")
    private String name;
    @ExcelProperty(index = 4)
    private Date birthday;
    @ExcelProperty(index = 5)
    private String phone;
    @ExcelProperty(index = 6)
    private String email;
    @ExcelIgnore
    private Integer isDelete;
    @ExcelProperty(index = 7)
    private String address;
}

执行结果:

2.1.4 读取多个sheet

我们前面用户信息基础再加了一个sheet2接着存用户信息,同时新增一个sheet=银行信息存储银行账户信息:

银行账号信息类:

@Data
public class Account {
    private String name;
    private String idCard;
    private String cardNo;
    private BigDecimal amount;
    private Integer status;
    private Date lastUsedTime;
}

如果我们没有银行信息这个sheet,我们可以用如下代码读写所有sheet:因为每个sheet的数据格式是一样的

        // 读取所有sheet
        EasyExcel.read(fileName, User.class, new UserExcelListener()).doReadAll();

每个sheet的数据格式不一样,只能单独一一处理,新建一个处理银行账号信息的监听器:

@Slf4j
public class AccountExcelListener extends AnalysisEventListener<Account> {
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.error("======>>>解析异常:", exception);
    }
​
    @Override
    public void invoke(Account data, AnalysisContext context) {
        log.info("解析到一条数据:{}", data);
    }
​
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("sheet={} 所有数据解析完成!", context.readSheetHolder().getSheetName());
    }
  
      /**
     * 解析表头数据
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
       log.info("表头数据:{}", ConverterUtils.convertToStringMap(headMap, context));
    }
}
​

测试用例方法改为如下:

    @Test
    public void testExcelRead() {
        String fileName = "/Users/shepherdmy/Desktop/testExcel.xlsx";
        // 读取用户信息两个sheet
        EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet(0).doRead();
        EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet(1).doRead();
        // 读取银行账户信息
        EasyExcel.read(fileName, Account.class, new AccountExcelListener()).sheet(2).doRead();
    }

执行结果:

解析到一条数据:User(id=1, userNo=uno:she001, gender=2, name=王小二, birthday=Fri Apr 12 10:00:00 CST 2024, phone=123456789, [email protected], isDelete=null, address=杭州市余杭区未来科技城)
解析到一条数据:User(id=2, userNo=uno:she002, gender=2, name=张三, birthday=Wed Apr 10 18:00:00 CST 2024, phone=987654321, [email protected], isDelete=null, address=杭州市拱墅区城西银泰)
解析到一条数据:User(id=3, userNo=uno:she003, gender=2, name=李四, birthday=Thu Apr 11 18:00:00 CST 2024, phone=345686789, [email protected], isDelete=null, address=上海陆家嘴浦东)
sheet=用户信息 所有数据解析完成!
解析到一条数据:User(id=5, userNo=uno:she005, gender=2, name=小五, birthday=Fri Apr 12 10:00:00 CST 2024, phone=123456789, [email protected], isDelete=null, address=成都太古里)
解析到一条数据:User(id=6, userNo=uno:she006, gender=2, name=小六, birthday=Wed Apr 10 18:00:00 CST 2024, phone=987654321, [email protected], isDelete=null, address=上海南京路)
解析到一条数据:User(id=7, userNo=uno:she007, gender=2, name=小七, birthday=Thu Apr 11 18:00:00 CST 2024, phone=345686789, [email protected], isDelete=null, address=杭州湖滨路)
sheet=用户信息2 所有数据解析完成!
表头数据:{0=姓名, 1=身份证号, 2=银行卡号, 3=余额, 4=状态, 5=最近使用时间}
解析到一条数据:Account(name=张三, idCard=3322114566, cardNo=62023445556, amount=10000.88, status=0, lastUsedTime=Sun Dec 31 00:00:00 CST 2023)
解析到一条数据:Account(name=李四, idCard=53134557, cardNo=623454576878, amount=6666.66, status=1, lastUsedTime=Sun Jun 11 00:00:00 CST 2023)
sheet=银行信息 所有数据解析完成!
​

3.EasyExcel数据导出

使用EasyExcel数据导出相对来说很简单,直接上代码,先来看看用户信息类User

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @ExcelProperty(index = 0)
    private Long id;
    @ExcelProperty(index = 1, converter = UserNoConverter.class)
    private String userNo;
    @ExcelProperty(value = "性别", converter = GenderConverter.class)
    private Integer gender;
    @ExcelProperty(value = "姓名")
    private String name;
    @ExcelProperty(index = 4)
    private Date birthday;
    @ExcelProperty(index = 5)
    private String phone;
    @ExcelProperty(index = 6)
    private String email;
    @ExcelIgnore
    private Integer isDelete;
    @ExcelProperty(index = 7)
    private String address;
}

我们前面说过使用@ExcelProperty时,不建议同时使用index和value去映射属性,我这里这么写只是让你看看导出效果来突出使用value@ExcelProperty(value = "姓名")的好处,话不多说直接看测试用例,我造了10条用户信息数据导出:

    @Test
    public void testExcelWrite() {
        List<User> userList = new ArrayList<>();
        int i = 0;
        while (i < 10) {
            User user = User.builder().id((long)i).userNo("no-" + i).birthday(new Date()).gender(i%3)
                    .phone("123456789"+i).email("[email protected]").name("芽儿哟"+i).address("杭州"+i).build();
            userList.add(user);
            i++;
        }
        String fileName = "export.xlsx";
        EasyExcel.write(fileName, User.class).sheet("员工信息").doWrite(userList);
​
    }

导出文件如下所示:

可以看到表头默认为属性名,使用@ExcelProperty(value = "姓名")就使用value更符合我们日常需求习惯。

假如导出大批量数据,我们不能一次性查出上百万条数据放入内存一次性导出,很有可能导致OOM,只能分页查询分批导出,实现如下:还是以上面的10天用户信息数据模拟,每3条处理一次导出

   @Test
    public void testExcelWrite() {
        List<User> userList = new ArrayList<>();
        int i = 0;
        while (i < 10) {
            User user = User.builder().id((long)i).userNo("no-" + i).birthday(new Date()).gender(i%3)
                    .phone("123456789"+i).email("[email protected]").name("芽儿哟"+i).address("杭州"+i).build();
            userList.add(user);
            i++;
        }
        String fileName = "export.xlsx";
​
        // 导出大批量数据,我们不能一次查询所有数据到内存中,只能分页查询导出,这里UserList假设需要分页导出,每页查询3条
        int size = userList.size();
        int page = size/ 3 + (size % 3 == 0 ? 0 : 1);
//        // 创建excelWriter
//        ExcelWriter excelWriter = EasyExcel.write(fileName, User.class).build();
//        // 创建writeSheet
//        WriteSheet writeSheet = EasyExcel.writerSheet(0,"分页员工信息").build();
//        for (int k = 0; k < page; k++) {
//            int start = k * 3;
//            int end = (k + 1) * 3 >= size ? size : (k + 1) * 3;
//            // 这里其实应该是分页查询数据,下面只是简单模拟下
//            List<User> users = userList.subList(start, end);
//            excelWriter.write(users, writeSheet);
//        }
//        // 关闭io才会写入数据,必须执行,所以最好用try-with-source安全,如下
//        excelWriter.finish();
​
        // 官方安全写法
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, User.class).build()) {
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            // 去调用写入,实际使用时根据数据库分页的总的页数来
            for (int k = 0; k < page; k++) {
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                int start = k * 3;
                int end = (k + 1) * 3 >= size ? size : (k + 1) * 3;
                List<User> users = userList.subList(start, end);
                excelWriter.write(users, writeSheet);
            }
        }
    }

动态表头和动态数据

上面的示例都是映射Java类的,其实我可以直接导出excel,表头数据无外乎就是第一行,我们可以灵活设置即可:

  @Test
    public void testExport() {
        List<String> heads = ListUtils.newArrayList("员工编号", "姓名", "性别", "手机号", "金额");
        // 表头
        List<List<String>> dataList = new ArrayList<>();
        dataList.add(heads);
        int i = 0;
        while (i < 10) {
            i++;
            dataList.add(ListUtils.newArrayList(String.valueOf(i), "王"+i, i%2 == 0 ? "男" : "女", "1234456", "666.88"));
        }
​
        String fileName = "export.xlsx";
        EasyExcel.write(fileName).sheet("员工").doWrite(dataList);
​
    }

更多导出实现比如说设置单元格样式颜色啥的,可查看官方文档:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write

4.EasyExcel与poi对比

EasyExcel 和 Apache POI 是两个常用的 Java 库,用于处理 Excel 文件。它们有着一些相似之处,同时也有一些不同之处。以下是它们之间的对比:

易用性

  • EasyExcel:EasyExcel 提供了简单易用的 API,可以方便地进行 Excel 文件的读写操作。它使用了注解来简化开发者的操作,支持直接将 Java 对象写入 Excel 文件,也支持将 Excel 文件转换为 Java 对象。
  • Apache POI:Apache POI 是一个功能强大但相对复杂的库,需要较多的代码来完成 Excel 文件的读写操作。使用 POI 进行 Excel 操作通常需要处理大量的低级 API 调用和数据转换。

性能

  • EasyExcel:EasyExcel 在性能上表现优异,通过内存映射等技术来优化文件的读写操作,可以处理大型 Excel 文件而不会出现内存溢出等问题。
  • Apache POI:Apache POI 在处理大型 Excel 文件时可能会出现性能问题,特别是在写入大量数据时,可能会占用大量的内存和 CPU 资源。

功能丰富程度

  • EasyExcel:EasyExcel 提供了丰富的功能,支持多种样式、合并单元格、图片插入等功能,同时还提供了注解和模板的方式来简化开发者的操作。
  • Apache POI:Apache POI 是一个功能强大的库,提供了丰富的 API 来操作 Excel 文件,支持多种格式的 Excel 文件读写,并且可以进行复杂的操作,如图表、公式、宏等。

社区和文档支持

  • EasyExcel:EasyExcel 是阿里巴巴开源的项目,拥有活跃的社区和丰富的文档支持。开发者可以在社区中获取到丰富的使用经验和问题解决方案。
  • Apache POI:Apache POI 是 Apache 软件基金会的项目,拥有庞大的社区和完善的文档支持。开发者可以在官方网站上找到详尽的文档和示例代码。

总的来说,EasyExcel 相对于 Apache POI 更易于使用且性能更好,特别适合处理大型 Excel 文件和简单的数据导出导入需求。而 Apache POI 则更加灵活,支持更多复杂的 Excel 操作,适用于对功能要求较高的场景。选择使用哪个库取决于具体的需求和开发者的偏好

5.总结

EasyExcel 是一个功能强大、易于使用的 Excel 操作工具,它大大简化了 Excel 数据的读取和写入操作,提高了开发效率。通过本文的介绍,相信读者对 EasyExcel 库有了更深入的了解,希望能够在实际开发中更多地利用 EasyExcel 提升工作效率。