EasyExcel保姆级教程(1)

8 篇文章 0 订阅

由于博猪所在行业使用到关于Excel导入、导出的功能比较多,本文主要详细介绍一下博猪使用的阿里巴巴的easyExcel,让Excel相关操作没那么多烦恼。

前言

Excel的缺点

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

  • Excel读写时候内存溢出

虽然POI是目前使用最多的用来做excel解析的框架,但这个框架并不那么完美。大部分使用POI都是使用他的userModel模式。userModel的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,相对比较好理解。然而userModel模式最大的问题是在于非常大的内存消耗,一个几兆的文件解析要用掉上百兆的内存。现在很多应用采用这种模式,之所以还正常在跑一定是并发不大,并发上来后一定会OOM或者频繁的full gc

  • 其他开源框架使用复杂

对POI有过深入了解的估计才知道原来POI还有SAX模式。但SAX模式相对比较复杂,excel有03和07两种版本,两个版本数据存储方式截然不同,sax解析方式也各不一样。想要了解清楚这两种解析方式,才去写代码测试,估计两天时间是需要的。再加上即使解析完,要转换到自己业务模型还要很多繁琐的代码。总体下来感觉至少需要三天,由于代码复杂,后续维护成本巨大。

  • 其他开源框架存在一些BUG修复不及时

EasyExcel简介

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。

阿里巴巴GitHub地址:Github

EasyExcelDemo

项目准备

  • 创建空的Maven项目
  • 添加Maven依赖
        <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.6</version>
        </dependency>
        <!--lombok插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.6</version>
            <optional>true</optional>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
  • 新建util包,创建FileUtil工具类
public class FileUtil {
    /**
     * 读取文件成输入流
     * @param fileName   文件名称
     * @return
     */
    public static InputStream getResourcesFileInputStream(String fileName) {
        return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName);
    }

    /**
     * 获取文件资源根路径,resource
     * @return
     */
    public static String getPath() {
        return FileUtil.class.getResource("/").getPath();
    }

    /**
     * 创建resource下文件
     * @param pathName   路径名称
     * @return
     */
    public static File createNewFile(String pathName) {
        File file = new File(getPath() + pathName);
        if (file.exists()) {
            file.delete();
        } else {
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
        }
        return file;
    }

    /**
     * 读取文件
     * @param pathName    全路径文件名
     * @return
     */
    public static File readFile(String pathName) {
        return new File(getPath() + pathName);
    }

    /**
     * 读取用户目录下面的文件
     * @param pathName    全路径文件名
     * @return
     */
    public static File readUserHomeFile(String pathName) {
        return new File(System.getProperty("user.home") + File.separator + pathName);
    }
}

Excel操作-读

测试类相关常量如下:

private final String SUFFIX_EXCEL_FILE_TYPE = ".xlsx";
private final String READ_ROOT_RESOURCE = "read";

最简单的读

对象
@Getter
@Setter
@ToString
public class AnnotationReadEntity {
    /**
     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
     */
    @ExcelProperty("字符串标题")
    private String title;
    @ExcelProperty("日期标题")
    private Date dateTitle;
    /**
     * 数字标题
     * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
     */
    @ExcelProperty(index = 2)
    private Double numberTitle;
}
监听器
public class SimpleReadListener extends AnalysisEventListener<SimpleReadEntity> {

    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleReadListener.class);

    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<SimpleReadEntity> saveList = new ArrayList<SimpleReadEntity>();

    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private EntityDao entityDao;
    public SimpleReadListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        this.entityDao = new EntityDao();
    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     * @param demoDAO
     */
    public SimpleReadListener(EntityDao demoDAO) {
        this.entityDao = demoDAO;
    }

    /**
     * 这个每一条数据解析都会来调用
     * @param simpleReadEntity
     *            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param analysisContext
     */
    public void invoke(SimpleReadEntity simpleReadEntity, AnalysisContext analysisContext) {
        LOGGER.info("解析到一条数据:{}", JSONObject.toJSONString(simpleReadEntity));
        saveList.add(simpleReadEntity);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (saveList.size() >= BATCH_COUNT) {
            batchSave();
            // 存储完成清理 list
            saveList.clear();
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
       // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        batchSave();
        LOGGER.info("所有数据解析完成!");
    }

    /**
     * 批量保存方法
     */
    private void batchSave() {
        if (!saveList.isEmpty()) {
            LOGGER.info("{}条数据,开始存储数据库!", saveList.size());
            entityDao.batchSave(saveList);
            LOGGER.info("存储数据库成功!");
        }
    }
}

持久层
public class EntityDao<T> {

    private static final Logger LOGGER = LoggerFactory.getLogger(EntityDao.class);

    public void batchSave(List<T> list) {
        LOGGER.info(">>>>>>>>>>>>>>EntityDao.batchSave:{}" + JSONObject.toJSONString(list));
    }
}
测试代码
    /**
     * 最简单的读
     * <p>1. 创建excel对应的实体对象 参照{@link SimpleReadEntity}
     * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link SimpleReadListener}
     * <p>3. 直接读即可
     */
    @Test
    public void testSimpleRead1() {
        // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
        // 写法1:
        String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, SimpleReadEntity.class, new SimpleReadListener()).sheet().doRead();
    }
    /**
     * 简单度方式2
     */
    @Test
    public void testSimpleRead2() {
        String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
        ExcelReader excelReader = null;
        try {
            excelReader = EasyExcel.read(fileName, SimpleReadEntity.class, new SimpleReadListener()).build();
            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            excelReader.read(readSheet);
        } finally {
            if (excelReader != null) {
                // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
                excelReader.finish();
            }
        }
    }

根据注解读取

excel示例

参照:excel示例

对象
@Getter
@Setter
@ToString
public class AnnotationReadEntity {
    /**
     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
     */
    @ExcelProperty("字符串标题")
    private String title;
    @ExcelProperty("日期标题")
    private Date dateTitle;
    /**
     * 数字标题
     * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
     */
    @ExcelProperty(index = 2)
    private Double numberTitle;
}
监听器
public class AnnotationReadListener extends AnalysisEventListener<AnnotationReadEntity> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationReadListener.class);

    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<AnnotationReadEntity> saveList = new ArrayList<AnnotationReadEntity>();

    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private EntityDao entityDao;
    public AnnotationReadListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        this.entityDao = new EntityDao();
    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     * @param demoDAO
     */
    public AnnotationReadListener(EntityDao demoDAO) {
        this.entityDao = demoDAO;
    }

    /**
     * 这个每一条数据解析都会来调用
     * @param entity
     *            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param analysisContext
     */
    public void invoke(AnnotationReadEntity entity, AnalysisContext analysisContext) {
        LOGGER.info("解析到一条数据:", entity);
        saveList.add(entity);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (saveList.size() >= BATCH_COUNT) {
            batchSave();
            // 存储完成清理 list
            saveList.clear();
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     * @param analysisContext
     */
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
       // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        batchSave();
        LOGGER.info("所有数据解析完成!");
    }

    /**
     * 批量保存方法
     */
    private void batchSave() {
        if (!saveList.isEmpty()) {
            LOGGER.info("{}条数据,开始存储数据库!", saveList.size());
            entityDao.batchSave(saveList);
            LOGGER.info("存储数据库成功!");
        }
    }
}

测试代码
    /**
     * 注解读取,与标题不匹配的名称可忽略
     */
    @Test
    public void testAnnotationRead() {
        String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "annotationRead" + SUFFIX_EXCEL_FILE_TYPE;
        ExcelReader excelReader = null;
        try {
            excelReader = EasyExcel.read(fileName, AnnotationReadEntity.class, new AnnotationReadListener()).build();
            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            excelReader.read(readSheet);
        } finally {
            if (excelReader != null) {
                // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
                excelReader.finish();
            }
        }
    }

读多个sheet

测试代码
    /**
     * 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link SimpleReadEntity}
     * <p>
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link SimpleReadListener}
     * <p>
     * 3. 直接读即可
     */
    @Test
    public void testRepeatedRead() {
        String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
        // 读取全部sheet
        // 这里需要注意 SimpleReadListener 的 doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个 SimpleReadListener 里面写
        EasyExcel.read(fileName, SimpleReadEntity.class, new SimpleReadListener()).doReadAll();

        // 读取部分sheet
        fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
        ExcelReader excelReader = null;
        try {
            excelReader = EasyExcel.read(fileName).build();

            // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener
            ReadSheet readSheet1 =
                    EasyExcel.readSheet(0).head(SimpleReadEntity.class).registerReadListener(new SimpleReadListener()).build();
            ReadSheet readSheet2 =
                    EasyExcel.readSheet(1).head(SimpleReadEntity.class).registerReadListener(new SimpleReadListener()).build();
            // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
            excelReader.read(readSheet1, readSheet2);
        } finally {
            if (excelReader != null) {
                // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
                excelReader.finish();
            }
        }
    }

读取多行

测试代码
/**
 * 多行头
 * <p>1. 创建excel对应的实体对象 参照{@link SimpleReadEntity}
 * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link SimpleReadListener}
 * <p>3. 设置headRowNumber参数,然后读。 这里要注意headRowNumber如果不指定, 会根据你传入的class的{@link ExcelProperty#value()}里面的表头的数量来决定行数,
 * 如果不传入class则默认为1.当然你指定了headRowNumber不管是否传入class都是以你传入的为准。
 */
@Test
public void testManyHeaderRead() {
    String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
    // 这里 需要指定读用哪个class去读,然后读取第一个sheet
    EasyExcel.read(fileName, SimpleReadEntity.class, new SimpleReadListener()).sheet()
            // 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行
            .headRowNumber(1).doRead();
}

同步返回数据

/**
 * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面
 */
@Test
public void testSyncResRead() {
    String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
    // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
    List<SimpleReadEntity> list = EasyExcel.read(fileName).head(SimpleReadEntity.class).sheet().doReadSync();
    for (SimpleReadEntity data : list) {
        System.out.println("读取到数据: " + JSON.toJSONString(data));
    }
    // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish
    List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
    for (Map<Integer, String> data : listMap) {
        // 返回每条数据的键值对 表示所在的列 和所在列的值
        System.out.println(JSON.toJSONString(data));
        System.out.println("读取到数据: " + JSON.toJSONString(data));
    }
}

读取表头数据

SimpleReadListener监听器上增加方法
/**
 * 这里会一行行的返回头
 *
 * @param headMap
 * @param context
 */
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
    LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
}
测试代码
/**
 * 读取表头数据
 *
 * <p>
 * 1. 创建excel对应的实体对象 参照{@link SimpleReadEntity}
 * <p>
 * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link SimpleReadListener}
 * <p>
 * 3. 直接读即可
 */
@Test
public void testHeaderRead() {
    String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
    // 这里 需要指定读用哪个class去读,然后读取第一个sheet
    EasyExcel.read(fileName, SimpleReadEntity.class, new SimpleReadListener()).sheet().doRead();
}

异常处理

对象
@Getter
@Setter
@ToString
public class ExceptionReadEntity {
    /** 标题 */
    private String title;
    /**
     * 用日期去接字符串 肯定报错
     */
    private String dateTitle;
    /** 数字标题 */
    private Double numberTitle;
}
SimpleReadListener监听器上增加方法
/**
 * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
 * @param exception
 * @param context
 * @throws Exception
 */
@Override
public void onException(Exception exception, AnalysisContext context){
    LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
    // 如果是某一个单元格的转换异常 能获取到具体行号
    // 如果要获取头的信息 配合invokeHeadMap使用
    if (exception instanceof ExcelDataConvertException) {
        ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
        LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
                excelDataConvertException.getColumnIndex());
    }
}
测试代码
/**
 * 数据转换等异常处理
 *
 * <p>
 * 1. 创建excel对应的实体对象 参照{@link ExceptionReadEntity}
 * <p>
 * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link SimpleReadListener}
 * <p>
 * 3. 直接读即可
 */
@Test
public void testSimpleReadOnException() {
    String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
    // 这里 需要指定读用哪个class去读,然后读取第一个sheet
    EasyExcel.read(fileName, ExceptionReadEntity.class,new SimpleReadListener()).sheet().doRead();
}

测试不创建对象的读取

监听器
public class NotCreateObjectReadListener  extends AnalysisEventListener<Map<Integer, String>> {

    private static final Logger LOGGER = LoggerFactory.getLogger(NotCreateObjectReadListener.class);

    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<Map<Integer, String>> list = new ArrayList<Map<Integer, String>>();

    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
        list.add(data);
        if (list.size() >= BATCH_COUNT) {
            saveData();
            list.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        saveData();
        LOGGER.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        LOGGER.info("{}条数据,开始存储数据库!", list.size());
        LOGGER.info("存储数据库成功!");
    }
}
测试代码
@Test
public void testNotCreateObjectRead() {
    String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
    // 这里 只要,然后读取第一个sheet 同步读取会自动finish
    EasyExcel.read(fileName, new NotCreateObjectReadListener()).sheet().doRead();
}

格式转换

对象
@Getter
@Setter
@ToString
public class ConverterReadEntity {
    /**
     * 我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:”
     */
    @ExcelProperty(converter = CustomStringConverter.class)
    private String title;
    /**
     * 这里用string 去接日期才能格式化。我想接收年月日格式
     */
    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
    private String dateTitle;
    /**
     * 我想接收百分比的数字
     */
    @NumberFormat("#.##%")
    private Double numberTitle;
}
监听器
public class ConverterReadListener extends AnalysisEventListener<ConverterReadEntity> {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConverterReadListener.class);

    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<ConverterReadEntity> saveList = new ArrayList<ConverterReadEntity>();

    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private EntityDao entityDao;
    public ConverterReadListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        this.entityDao = new EntityDao();
    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     * @param demoDAO
     */
    public ConverterReadListener(EntityDao demoDAO) {
        this.entityDao = demoDAO;
    }

    /**
     * 这个每一条数据解析都会来调用
     * @param entity
     *            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param analysisContext
     */
    public void invoke(ConverterReadEntity entity, AnalysisContext analysisContext) {
        LOGGER.info("解析到一条数据:", entity);
        saveList.add(entity);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (saveList.size() >= BATCH_COUNT) {
            batchSave();
            // 存储完成清理 list
            saveList.clear();
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     * @param analysisContext
     */
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
       // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        batchSave();
        LOGGER.info("所有数据解析完成!");
    }

    /**
     * 批量保存方法
     */
    private void batchSave() {
        if (!saveList.isEmpty()) {
            LOGGER.info("{}条数据,开始存储数据库!", saveList.size());
            entityDao.batchSave(saveList);
            LOGGER.info("存储数据库成功!");
        }
    }
}
测试代码
/**
 * 日期、数字或者自定义格式转换
 * <p>
 * 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()}
 * <p>1. 创建excel对应的实体对象 参照{@link ConverterReadEntity}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解
 * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterReadListener}
 * <p>3. 直接读即可
 */
@Test
public void  testConvertRead() {
    String fileName = FileUtil.getPath() + READ_ROOT_RESOURCE + File.separator + "simpleRead" + SUFFIX_EXCEL_FILE_TYPE;
    // 这里 需要指定读用哪个class去读,然后读取第一个sheet
    EasyExcel.read(fileName, ConverterReadEntity.class, new ConverterReadListener())
            // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。
            // 如果就想单个字段使用请使用@ExcelProperty 指定converter
            // .registerConverter(new CustomStringStringConverter())
            // 读取sheet
            .sheet().doRead();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值