easyExcel類(Excel解析工具)

一、 介紹

Java解析、生成Excel比較有名的框架有Apache poi、jxl。但他們都存在一個(gè)嚴(yán)重的問題就是非常的耗內(nèi)存,poi有一套SAX模式的API可以一定程度的解決一些內(nèi)存溢出的問題,但POI還是有一些缺陷,比如07版Excel解壓縮以及解壓后存儲(chǔ)都是在內(nèi)存中完成的,內(nèi)存消耗依然很大。easyexcel重寫了poi對(duì)07版Excel的解析,一個(gè)3M的excel用POI sax解析依然需要100M左右內(nèi)存,改用easyexcel可以降低到幾M,并且再大的excel也不會(huì)出現(xiàn)內(nèi)存溢出;03版依賴POI的sax模式,在上層做了模型轉(zhuǎn)換的封裝,讓使用者更加簡(jiǎn)單方便

版本支持
2+版本支持Java7&Java6
3+版本支持Java8

詳細(xì)了解可以點(diǎn)擊官網(wǎng)文檔查閱

個(gè)人依賴:

<dependencies>
        <!--easyexcel-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
        </dependency>
        <!--slf4j 日志-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>

</dependencies>

二、 讀Excel

Excel:


對(duì)象:

@Data
public class Question1 {
    private String id;            //題目ID
    private String companyId;   //所屬企業(yè)
    private String catalogId;   //題目所屬目錄ID
}

監(jiān)視器:

名稱 說明
AnalysisEventListener<T> 分析事件偵聽器:接收解析的每條數(shù)據(jù)的返回
SyncReadListener 同步讀取偵聽器
AbstractIgnoreExceptionReadListener 抽象忽略異常讀取偵聽器
ModelBuildEventListener 模型構(gòu)建事件偵聽器

AnalysisEventListener<T>
所有已實(shí)現(xiàn)的接口:Listener、ReadListener
直接已知子類:SyncReadListener

方法 返回值類型 說明
boolean hasNext 驗(yàn)證是否有另一條數(shù)據(jù)。您可以通過返回 false 來(lái)停止讀取
void invokeHead 分析第一行時(shí)觸發(fā)調(diào)用函數(shù)
void invokeHeadMap 以map的形式放回表頭,覆蓋當(dāng)前方法以接收表頭數(shù)據(jù)
void onException 當(dāng)任何一個(gè)監(jiān)聽器進(jìn)行錯(cuò)誤報(bào)告時(shí),所有監(jiān)聽器都會(huì)收到此方法

代碼:

 private static void read2() {
        final List list = new ArrayList();
        //使用EasyExcel讀取test1.xlsx文件 
        EasyExcel.read("../test/test1.xlsx", Question1.class, new AnalysisEventListener<Question1>() {
                    //重寫子類方法
                    @Override
                    public void invoke(Question1 question1, AnalysisContext analysisContext) {
                        list.add(question1);
                    }
                    //重寫子類方法
                    @Override
                    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

                    }
          
                    @Override
                    public void invokeHeadMap(Map headMap, AnalysisContext context) {
                        System.out.println(headMap);
                    }
                }
        ).doReadAll();

        //獲取讀取到的數(shù)據(jù)
        for (Object o : list) {
            Question1 question1 = (Question1) o;
            System.out.println(question1);
        }

    }

結(jié)果視圖:


SyncReadListener
所有已實(shí)現(xiàn)的接口:Listener、ReadListener
直接已知父類:AnalysisEventListener<T>

方法 返回值類型 說明
void doAfterAllAnalysed 如果有什么操作在全部分析結(jié)束后執(zhí)行
List<Object> getList
void invoke 當(dāng)分析一行觸發(fā)器調(diào)用函數(shù)
void setList(List<Object> list)

代碼:

private static void read1() {
        final List list = new ArrayList();
        //使用EasyExcel讀取test1.xlsx文件
        EasyExcel.read("../test/test1.xlsx", Question1.class, new SyncReadListener() {
            //EasyExcel在讀取excel表格時(shí),每讀取到一行,就會(huì)調(diào)用一次這個(gè)方法,
            //并且將讀取到的行數(shù)據(jù),封裝到指定類型(Question1)的對(duì)象中,傳遞給我們(Object object)
            /*
            此問題可能出現(xiàn)在低版本的easyExcel中,出現(xiàn)時(shí)可以按照下列方式解決
                如果表格數(shù)據(jù)不是頂行寫的,需要通過headRowNumber指定表頭行的數(shù)量
                如果表格數(shù)據(jù)不是頂列寫的,需要在封裝的實(shí)體屬性上通過@ExcelProperty將實(shí)體屬性和表格列名進(jìn)行對(duì)應(yīng)
             */
            @Override
            public void invoke(Object object, AnalysisContext context) {
//                System.out.println(object);
                list.add(object);
            }
        }).doReadAll();

        //獲取讀取到的數(shù)據(jù)
        for (Object o : list) {
            Question1 question1 = (Question1) o;
            System.out.println(question1);
        }

    }

解決方式

    //index屬性指定當(dāng)前這個(gè)對(duì)應(yīng)的是表格中哪個(gè)索引的列,表格中的索引是從0開始的
    /**
     * 強(qiáng)制讀取第三個(gè) 這里不建議 index 和 name 同時(shí)用,要么一個(gè)對(duì)象只用index,要么一個(gè)對(duì)象只用name去匹配
     */
    @ExcelProperty(index = 2)
    private String catalogId;   //題目所屬目錄ID
    /**
     * 用名字去匹配,這里需要注意,如果名字重復(fù),會(huì)導(dǎo)致只有一個(gè)字段讀取到數(shù)據(jù)
     */
    @ExcelProperty("題目id")
    private String id;      //題目ID
    @ExcelProperty("企業(yè)id")
    private String companyId;   //所屬企業(yè)

三、 寫Excel (比較常用)

3.1、簡(jiǎn)單入門


    private static void write() {
        List list = new ArrayList();
        list.add(new Question1("1", "1", "1"));
        list.add(new Question1("2", "1", "1"));
        list.add(new Question1("3", "1", "1"));
  1.
        EasyExcel
                .write("test/test_w.xlsx", Question1.class)
                .sheet()
                .doWrite(list);
    

  2.
    ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write("test/test_w.xlsx", Question1.class).build();
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(list, writeSheet);
        } finally {
            // 千萬(wàn)別忘記finish 會(huì)幫忙關(guān)閉流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

3.2、 指定寫入的列
添加該實(shí)體列的注釋就可以實(shí)現(xiàn)如下:

    @ExcelProperty(value = "題目id", index = 0)
    private String id;      //題目ID
    @ExcelProperty(value = "企業(yè)id", index = 1)
    private String companyId;   //所屬企業(yè)
    @ExcelProperty(value = "類目id", index = 3)
    private String catalogId;   //題目所屬目錄ID
指定寫入的列

3.2、 復(fù)雜表頭
添加該實(shí)體列的注釋就可以實(shí)現(xiàn)如下:

    @ExcelProperty({"主標(biāo)題", "題目id"})
    private String id;      //題目ID
    @ExcelProperty({"主標(biāo)題", "企業(yè)id"})
    private String companyId;   //所屬企業(yè)
    @ExcelProperty({"主標(biāo)題", "類目id"})
    private String catalogId;   //題目所屬目錄ID
復(fù)雜表頭

3.3、 列寬 行高
添加該實(shí)體列的注釋就可以實(shí)現(xiàn)如下:

  @Data
  @HeadRowHeight(30) //表頭行高
  @ContentRowHeight(20) //數(shù)據(jù)行高
  @ColumnWidth(25) //列寬
  public class Question1 {

    @ExcelProperty({"題目id"})
    private String id;      //題目ID
    @ExcelProperty({"企業(yè)id"})
    private String companyId;   //所屬企業(yè)
    //局部定義
    @ColumnWidth(50)
    @ExcelProperty({"類目id"})
    private String catalogId;   //題目所屬目錄ID
  }
  • 自定義列寬 行高

三、填充Excel

3.1、基于模板填充表格數(shù)據(jù)

  1. 寫出多條記錄
private static void write_template_multi() {
        List list = new ArrayList();
        list.add(new Question1("1", "1", "1"));
        list.add(new Question1("2", "1", "1"));
        list.add(new Question1("3", "1", "1"));

        EasyExcel
                .write("../test/test_w.xlsx", Question1.class)
                .withTemplate("../test/test_template.xlsx")
                .sheet()
                .doFill(list);
    }
  • 多條數(shù)據(jù)

2.寫出一條記錄

private static void write_template_one() {
        EasyExcel
                .write("test/test_w.xlsx", Question1.class)
                .withTemplate("test/test_template_one.xlsx")
                .sheet()
                .doFill(new Question1("1", "1", "1"));
    }
  • 一條數(shù)據(jù)

添加一條數(shù)據(jù)與添加多條數(shù)據(jù)的差別如下:
模板中參數(shù)中的前是否有.,若有則能多或單條條數(shù)據(jù)寫入;否則只允許單條數(shù)據(jù)寫入

  • 一條模板
  • 多條模板

3.1、復(fù)雜的填充

private static void write_template_multi_one() {
        List list = new ArrayList();
        list.add(new Question1("1", "1", "1"));
        list.add(new Question1("2", "1", "1"));
        list.add(new Question1("3", "1", "1"));

        ExcelWriterBuilder writerBuilder = EasyExcel
                .write("../test/test_w.xlsx", Question1.class)
                .withTemplate("../test/test_template_mul_one.xlsx");

        WriteSheet writeSheet = writerBuilder.sheet().build();
        ExcelWriter excelWriter = writerBuilder.build();

        //在填充玩多結(jié)果數(shù)據(jù)后,要強(qiáng)制換行,不然后續(xù)的單結(jié)果數(shù)據(jù)會(huì)發(fā)生覆蓋現(xiàn)象
        FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();

        excelWriter.fill(list, fillConfig, writeSheet);

        Map map = new HashMap();
        map.put("total", 600);
        excelWriter.fill(map, writeSheet);

        //調(diào)用finish方法
        excelWriter.finish();
    }

四、常見API

4.1、關(guān)于常見類解析

類名 說明
EasyExcel 入口類,用于構(gòu)建開始各種操作
ExcelReaderBuilder ExcelWriterBuilder 構(gòu)建出一個(gè) ReadWorkbook WriteWorkbook,可以理解成一個(gè)excel對(duì)象,一個(gè)excel只要構(gòu)建一個(gè)
ExcelReaderSheetBuilder ExcelWriterSheetBuilder 構(gòu)建出一個(gè) ReadSheet WriteSheet對(duì)象,可以理解成excel里面的一頁(yè),每一頁(yè)都要構(gòu)建一個(gè)
ReadListener 在每一行讀取完畢后都會(huì)調(diào)用ReadListener來(lái)處理數(shù)據(jù)
WriteHandler 在每一個(gè)操作包括創(chuàng)建單元格、創(chuàng)建表格等都會(huì)調(diào)用WriteHandler來(lái)處理數(shù)據(jù)

所有配置都是繼承的,Workbook的配置會(huì)被Sheet繼承,所以在用EasyExcel設(shè)置參數(shù)的時(shí)候,在EasyExcel...sheet()方法之前作用域是整個(gè)sheet,之后針對(duì)單個(gè)sheet

4.2、讀
注解
ExcelProperty 指定當(dāng)前字段對(duì)應(yīng)excel中的那一列??梢愿鶕?jù)名字或者Index去匹配。當(dāng)然也可以不寫,默認(rèn)第一個(gè)字段就是index=0,以此類推。千萬(wàn)注意,要么全部不寫,要么全部用index,要么全部用名字去匹配。千萬(wàn)別三個(gè)混著用,除非你非常了解源代碼中三個(gè)混著用怎么去排序的。
ExcelIgnore 默認(rèn)所有字段都會(huì)和excel去匹配,加了這個(gè)注解會(huì)忽略該字段
DateTimeFormat 日期轉(zhuǎn)換,用String去接收excel日期格式的數(shù)據(jù)會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.SimpleDateFormat
NumberFormat 數(shù)字轉(zhuǎn)換,用String去接收excel數(shù)字格式的數(shù)據(jù)會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.DecimalFormat
ExcelIgnoreUnannotated 默認(rèn)不加ExcelProperty 的注解的都會(huì)參與讀寫,加了不會(huì)參與

4.2、寫
注解
ExcelProperty index 指定寫到第幾列,默認(rèn)根據(jù)成員變量排序。value指定寫入的名稱,默認(rèn)成員變量的名字,多個(gè)value可以參照快速開始中的復(fù)雜頭
ExcelIgnore 默認(rèn)所有字段都會(huì)寫入excel,這個(gè)注解會(huì)忽略這個(gè)字段
DateTimeFormat 日期轉(zhuǎn)換,將Date寫到excel會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.SimpleDateFormat
NumberFormat 數(shù)字轉(zhuǎn)換,用Number寫excel會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.DecimalFormat
ExcelIgnoreUnannotated 默認(rèn)不加ExcelProperty 的注解的都會(huì)參與讀寫,加了不會(huì)參與

五、其它相關(guān)特殊用法

六、文檔保護(hù)措施

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容