EasyExcel 框架使用-讀

EasyExcel 框架使用

官方介紹:JAVA解析Excel工具EasyExcel

Java解析、生成Excel比較有名的框架有Apache poi、jxl。但他們都存在一個嚴重的問題就是非常的耗內存,poi有一套SAX模式的API可以一定程度的解決一些內存溢出的問題,但POI還是有一些缺陷,比如07版Excel解壓縮以及解壓後存儲都是在內存中完成的,內存消耗依然很大。easyexcel重寫了poi對07版Excel的解析,能夠原本一個3M的excel用POI sax依然需要100M左右內存降低到幾M,並且再大的excel不會出現內存溢出,03版依賴POI的sax模式。在上層做了模型轉換的封裝,讓使用者更加簡單方便

EasyExcel 版本

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

在EasyExcel 使用之前先要講一下EasyExcel 的Listener

Listener 里有處理每一條數據的方法

ReadListener源碼:

public interface ReadListener<T> extends Listener {
    // 當解析拋出異常,會被此方法進行捕獲
    void onException(Exception exception, AnalysisContext context) throws Exception;
	// 解析表數據的表頭,headMap 默認表格第一行數據
    void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context);
    // 講一條數據解析後的對象 data,可以在這個方法中進行再次處理
    void invoke(T data, AnalysisContext context);
   	// 官方介紹:返回額外信息調用,沒使用過
    void extra(CellExtra extra, AnalysisContext context);
    // 解析完成後,會運行此方法
    void doAfterAllAnalysed(AnalysisContext context);
	// 校驗是否繼續解析,默認true
    boolean hasNext(AnalysisContext context);
}

這個方法會有一個傳入的泛型,就是將表格每一行數據解析成的對象

舉個例子:如果我們就想轉換成List,重寫AnalysisEventListener(ReadListener的一個實現類)就可以將List 傳入

我們就可以轉換了,當然官方有一個默認List的實現 NoModelDataListener

但是你想獲取解析表格的數據最好自己重寫AnalysisEventListener,這樣你能把我數據的處理流程

EasyExcel 使用

讀數據

EasyExcel 讀數據有許多不同的方法,我們使用NoModelDataListener來看一下可以怎麼解析表格數據

public void test() {

    // file 可以是文件,可以是流,也可以是文件路徑 但必須是.xsl或.xslx文件
    File file = new File("");

    // 讀取讀取傳入excel文件中某一張表的數據
    // 第一種:sheet() 默認(不傳參數)為第一種表
    // sheetNo 也可以指定第幾張表,表以0開始計數
    // sheetName 也可以指定表名
    // 讀取成功後文件流會自動關閉
    EasyExcel.read(file,new NoModelDataListener()).sheet(0,"").doRead();

    // 第一種:sheet() 默認為第一張表,也可以指定第幾張表,表以0開始計數
    ExcelReader reader = EasyExcel.read(file, new NoModelDataListener()).build();
    // 第一種:readSheet() 默認(不傳參數)為第一種表
    // sheetNo 也可以指定第幾張表,表以0開始計數
    // sheetName 也可以指定表名
    //sheet不能讀取多次,多次讀取需要重新讀取文件
    ReadSheet sheet = EasyExcel.readSheet().build();
    reader.read(sheet);
    // 記得關閉ExcelReader
    reader.finish();

    // EasyExcel 也可以替換成 EasyExcelFactory
    // EasyExcel 繼承 EasyExcelFactory,EasyExcel沒有對EasyExcelFactory進行任何重寫
    // 所以EasyExcel與EasyExcelFactory相當於同一個類


    // 讀取讀取傳入excel文件,所有的表數據

    // 第一種
    // 但是如果解析失敗拋出異常或者Listener的hasNext方法返回false後就不會繼續往下執行
    EasyExcel.read(file,new NoModelDataListener()).doReadAll();

    // 第二種
    // 但是如果解析失敗拋出異常,停止解析但是會關閉一些excelReader的數據,需要重新新建excelReader才能繼續循環執行
    // Listener的hasNext方法返回false後就不會繼續往下執行,不會出現上面的問題
    ExcelReader excelReader = EasyExcel.read(file, new NoModelDataListener()).build();
    List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
    for (ReadSheet readSheet : readSheets) {
        excelReader.read(readSheet);
    }
    // 記得關閉ExcelReader
    excelReader.finish();

}

自定義Listener

上面說了讀取方式,下面說一下自定義Listener

其實我們可以封裝一個通用的Listener

這是我自己封裝的Listener

@Slf4j
public class DefaultExcelListener<T> extends AnalysisEventListener<T> {
    // 解析後的數據
    private final List<T> rows = new ArrayList();
  	// 解析表數據的表頭,headMap 默認表格第一行數據
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        log.info("======================================================");
        log.info("解析第一行數據:{}" + JSON.toJSONString(headMap));
        log.info("======================================================");

    }

    // 講一條數據解析後的對象 data,將解析後的數據保存到 rows中
    @Override
    public void invoke(T object, AnalysisContext context) {
        rows.add(object);
    }

    // 讀取完excel數據後的操作
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("成功讀取【" + rows.size() + "】條數據");
    }

    // 在讀取excel異常 獲取其他異常下會調用本接口。拋出異常則停止讀取。如果這裡不拋出異常則 繼續讀取下一行。
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        System.out.println("解析失敗,但是繼續解析下一行:{}" + exception.getMessage());
    }

    public List<T> getRows() {
        return rows;
    }
}

我們傳入泛型對象(對象屬性添加EasyExcel 註解)就可以進行解析,最後獲取rows就可以得到解析後的數據

當然也可以根據自己的理解進行修改

使用中的問題

說一下在使用過程中遇到的問題

1.想要停止解析的問題

EasyExcel 想要解析需要拋出ExcelAnalysisStopException才能停止解析

例如你想檢驗表頭,表頭不匹配就不進行解析

 @Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
	// 不匹配
    throw new ExcelAnalysisStopException();
}

拋出異常後會被onException方法進行捕獲

那就在onException中再拋出ExcelAnalysisStopException就可以拋出

@Override
public void onException(Exception exception, AnalysisContext context) {
    if (exception instanceof ExcelAnalysisStopException) {
        throw new ExcelAnalysisStopException("");
    }
    System.out.println("解析失敗,但是繼續解析下一行:{}" + exception.getMessage());
}

但是就一些的版本是不能拋出這個異常,onException會被上層進行捕獲,重新拋 出一個異常

所以想要停止解析,通過hasNext是比較保險的

2.將表格中單元格數據進行轉換成對象時,不會校驗字符串「」

將單元格數據轉換成對象使用有專門的轉換器Converter

Converter接口

public interface Converter<T> {

    // 轉換後對象的類型
    Class supportJavaTypeKey();
	
    // 單元格數據類型
    CellDataTypeEnum supportExcelTypeKey();

   	// cellData單元格數據
    // T轉換後的對象
    // 將單元格數據轉化成對象:主要是讀的時候使用
    T convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
        GlobalConfiguration globalConfiguration) throws Exception;
 
    // 將對象轉化成對象單元格數據:主要是寫的時候使用
    CellData convertToExcelData(T value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration)
        throws Exception;
}

EasyExcel 有一些基礎類型的默認轉換器,也是繼承Converter接口

如果我們需要自定義轉換器也可以繼承這個接口對方法進行重寫

在EasyExcel 使用轉換器的時候會默認使用String轉基本對象的方法

但是其中沒有進行對字符串””的校驗,所以在轉換Data等對象時會報錯

我是自定義進行解決的,不知道是不是我使用方式不對,還是已經有解決但是我沒有使用到,如果大家發現是我沒有發現,希望能告訴我一下

EasyExcel 默認轉換器都是使用自定義的基礎類型utils進行轉換

image-20210610154510195

自定義Converter

說一下自定義Converter的使用

當我們自定義Converter後可以通過@ExcelProperty()指定對象中的某個屬性使用自定義轉換器
image-20210610153635992

也可以在EasyExcel.read()後指定

  EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener())
            // 這裡注意 我們也可以registerConverter來指定自定義轉換器, 但是這個轉換變成全局了, 所有java為string,excel為string的都會用這個轉換器。
            // 如果就想單個字段使用請使用@ExcelProperty 指定converter
             .registerConverter(new CustomStringStringConverter())
            // 讀取sheet
            .sheet().doRead();

@ExcelProperty()

用於屬性:與表頭進行匹配

image-20210610153635992

value表頭名稱,可以配置多級表頭

index表序列號

converter自定義轉換器,將單元格數據轉換成對象

到這裡我們就可以使用EasyExcel 進行讀取數據進行處理,目前我就使用到了EasyExcel 讀取,後面使用到寫數據再更新寫出數據

Tags: