多场景easyExcel读取excel文件(二)

本文介绍了阿里巴巴开源的EasyExcel框架,它以低内存消耗解析Excel,支持普通读取、多sheet处理、表头读取、额外信息读取、特定行读取以及异常处理。文章详细讲解了如何在项目中添加依赖、导入包和实现各种读取场景的代码示例。
摘要由CSDN通过智能技术生成

目录

一 EasyExcel简介

二 导出场景

pom依赖

代码导入包

1.普通读

2.读取多sheet

3.读取表头

4.读取额外信息 批注 超链接 合并的单元格

5.读取特定某行 

6.读取时异常处理


一 EasyExcel简介


Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知(AnalysisEventListener)

二 导出场景

为了方便大家理解,拦截器实现的都是ReadListener,其实用AnalysisEventListener更高效.大家在实际应用中可以换成AnalysisEventListener,实现改成继承就可以,可以参考普通读的监听器EasyExcelListener.

pom依赖
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>
代码导入包
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.minghe.jiaozhu.listener.*;
import com.minghe.jiaozhu.model.dto.ImportExcel;
import com.minghe.jiaozhu.service.ImportService;
import org.springframework.stereotype.Service;

1.普通读

代码

    public void importExcel(String path) {
        EasyExcelListener listener = new EasyExcelListener<>();
        //headRowNumber表头所在的行数
        EasyExcel.read(path, ImportExcel.class, listener).headRowNumber(3).sheet().doReadSync();
        List<ImportExcel> data = listener.getData();
        for (ImportExcel datum : data) {
            System.out.println(datum);
        }
    }

第二种实现方式

    public void importExcel(String path) {
        List<ImportExcel> dataList = EasyExcel.read(path)
                .sheet("sheetName")
                .headRowNumber(3)
                .head(ImportExcel.class)
                .doReadSync();
        for (ImportExcel importExcel : dataList) {
            System.out.println(importExcel);
        }
    }

这种方式好处是无需创建监听器

.headRowNumber(3) 这句代码意思是指定表头,将你传入的class和第三行匹配,第三行之后的都作为数据来读取.

监听器

@Data
public class EasyExcelListener<T>  extends AnalysisEventListener<T> {

    private List<T> data = new ArrayList<>();

    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        data.add(t);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

}

2.读取多sheet

    public void importSheetsExcel(String path) {
        EasyExcelListener listener = new EasyExcelListener();
        List<Class> clazzs = new ArrayList<>();
        clazzs.add(ImportExcel.class);
        clazzs.add(ImportExcel2.class);
        List<ReadSheet> readSheets = EasyExcel.read(path, listener).headRowNumber(1).build().excelExecutor().sheetList();
        List<List<Object>> dataList = new ArrayList<>();
        for (int i = 0; i < readSheets.size(); i++) {
            dataList.add(EasyExcel.read(path)
                    .sheet(i)
                    //这样就要求所有sheet页的表头的所在行数是一致的
                    .headRowNumber(1)
                    .head(clazzs.get(i))
                    .doReadSync());
        }
        for (List<Object> objects : dataList) {
            for (Object object : objects) {
                System.out.println(object);
            }
        }
    }

用这种方式读取多sheet要求所有表头所在行是一样的,第一个sheet表头在第一行的话,其他所有sheet的表头也都得在第一行.

还有一张方式,这种方式可以单独设定表头,但这种方式会把每个sheet的表头都读出来.真的需要这种方式的话,就把data喝data1的.get(0)去掉就行

/*    @Override
    public void importSheetsExcel(String path) {
        EasyExcelListener listener = new EasyExcelListener();
        EasyExcelListener listener2 = new EasyExcelListener();
        ExcelReader excelReader = EasyExcel.read(path, ImportExcel.class, listener).headRowNumber(3).build();

        ExcelReader excelReader1 = EasyExcel.read(path, ImportExcel2.class, listener2).headRowNumber(1).build();
        List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
        for (ReadSheet readSheet : readSheets) {
            String sheetName = readSheet.getSheetName();
            ReadSheet build = EasyExcel.readSheet(sheetName).build();
            excelReader.read(build);
            excelReader1.read(build);

        }
        excelReader.finish();
        excelReader1.finish();
        List<ImportExcel> data = listener.getData();
        for (ImportExcel importExcel : data) {
            System.out.println("1"+importExcel);
        }
        List<ImportExcel2> data1 = listener2.getData();
        for (ImportExcel2 importExcel2 : data1) {
            System.out.println("2"+importExcel2);
        }
    }*/

监听器 同上

3.读取表头

    @Override
    public void importExcelHead(String path) {
        EasyExcelHeadListener listener = new EasyExcelHeadListener<>();
        //headRowNumber表头所在的行数
        EasyExcel.read(path, ImportExcel.class, listener).headRowNumber(1).sheet().doReadSync();

        //数据
        List<ImportExcel> data = listener.getData();
        for (ImportExcel datum : data) {
            System.out.println(datum);
        }
        //表头
        //应该是headRowNumber设定的行数上方都算表头,都能读出来
        List<Map<Integer, String>> headerMapsList = listener.getHeaderMapsList();
        for (Map<Integer, String> integerStringMap : headerMapsList) {
            System.out.println(integerStringMap);
        }
    }

监听器

@Data
public class EasyExcelHeadListener<T> implements ReadListener<T> {

    private List<T> data = new ArrayList<>();
    private List<Map<Integer, String>> headerMapsList = new ArrayList<>();

    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        data.add(t);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        Map<Integer, String> integerStringMap = ConverterUtils.convertToStringMap(headMap, context);
        headerMapsList.add(integerStringMap);
    }

4.读取额外信息 批注 超链接 合并的单元格

代码

    public void importExcelExtra(String path) {
        //headRowNumber表头所在的行数
        EasyExcel.read(path, ImportExcel.class, new EasyExcelExtraListener()).headRowNumber(3)
                // 需要读取批注 默认不读取
                .extraRead(CellExtraTypeEnum.COMMENT)
                // 需要读取超链接 默认不读取
                .extraRead(CellExtraTypeEnum.HYPERLINK)
                // 需要读取合并单元格信息 默认不读取
                .extraRead(CellExtraTypeEnum.MERGE)
                .sheet()
                .doReadSync();
    }

监听器

@Data
@Slf4j
public class EasyExcelExtraListener<T>  implements ReadListener<T> {

    private List<T> data = new ArrayList<>();

    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        data.add(t);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
    @SneakyThrows
    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        log.info("读取到了一条额外信息:{}", JSON.toJSONString(extra));
        switch (extra.getType()) {
            case COMMENT:
                log.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(),
                        extra.getText());
                break;
            case HYPERLINK:
                if ("Sheet1!A1".equals(extra.getText())) {
                    log.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(),
                            extra.getColumnIndex(), extra.getText());
                } else if ("Sheet2!A1".equals(extra.getText())) {
                    log.info(
                            "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{},"
                                    + "内容是:{}",
                            extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
                            extra.getLastColumnIndex(), extra.getText());
                }
                break;
            case MERGE:
                log.info(
                        "额外信息是合并单元格,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}"
                        + "内容是:{}",
                        extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
                        extra.getLastColumnIndex(), extra.getText());
                break;
            default:
        }
    }

}

5.读取特定某行 

代码

    public void importExcelReadRow(String path) {
        //读取第一行第一列
        EasyExcelCellListener easyExcelCellListener = new EasyExcelCellListener(1, 1);
        EasyExcel.read(path, easyExcelCellListener)
                //headRowNumber(0)表头所在行要写0,不写的话默认第一行是表头
                .headRowNumber(0)
                .sheet()
                .doReadSync();
        String cellValue = easyExcelCellListener.getCellValue();
        System.out.println(cellValue);
    }

监听器

public class EasyExcelCellListener implements ReadListener<Map<Integer,String>> {

    private String cellValue;
    private int targetRow;
    private int targetColumn;

    private boolean isCellRead = false;

    public EasyExcelCellListener(int targetRow, int targetColumn) {
        this.targetRow = targetRow;
        this.targetColumn = targetColumn;
    }

    @Override
    //invoke方法的参数类型跟随实现的ReadListener的类型
    public void invoke(Map<Integer,String> map, AnalysisContext analysisContext) {
        if(isCellRead){
            return;
        }
        int currentRow = analysisContext.readRowHolder().getRowIndex()+1;
        if(currentRow == targetRow ){
            cellValue = map.get(targetColumn-1);
            isCellRead = true;
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

    public String getCellValue() {
        return cellValue;
    }


}

6.读取时异常处理

代码

    public void importExcelException(String path) {
        //headRowNumber表头所在的行数
        EasyExcel.read(path, ImportExcel.class, new EasyExcelExceptionListener())
                .headRowNumber(3)
                .sheet()
                .doReadSync();
    }

监听器

@Data
@Slf4j
public class EasyExcelExceptionListener<T>  implements ReadListener<T> {

    private List<T> data = new ArrayList<>();

    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        data.add(t);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
    @Override
    //只要补抛出异常就能继续解析
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        //这里只是转换异常,可以捕获其他异常
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
            excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
        }
    }
}

git仓库:导入导出: 导入导出的实例

多场景easyExcel导出excel文件(一):多场景easyExcel导出excel文件(一)-CSDN博客

 根据模板填充excel:多场景easyExcel根据模板填充excel文件(三)-CSDN博客

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值