EasyExcel

来源:https://www.cnblogs.com/V-Notes/archive/2023/06/02/17450635.html
-Advertisement-
Play Games

EasyExcel是一個基於Java的、快速、簡潔、解決大文件記憶體溢出的Excel處理工具。 他能讓你在不用考慮性能、記憶體的等因素的情況下,快速完成Excel的讀、寫等功能。 # 快速入門 導入依賴 ~~~xml com.alibaba easyexcel 3.1.1 ~~~ # 寫 Excel # ...


EasyExcel是一個基於Java的、快速、簡潔、解決大文件記憶體溢出的Excel處理工具。
他能讓你在不用考慮性能、記憶體的等因素的情況下,快速完成Excel的讀、寫等功能。

快速入門

導入依賴

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

寫 Excel

1.以實體類映射想要寫入 Excel 的數據

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExcelEntity {
    @ExcelProperty("字元串標題")
    private String string;
    @ExcelProperty("日期標題")
    private Date date;
    @ExcelProperty("數字標題")
    private Double doubleData;

    @ExcelIgnore
    private String ignore;
}

註解說明

  • ExcelProperty:定義排版

    • value:字元串標題

    • index:指定數據所在列,從0起算

      @ExcelProperty(value = "字元串標題", index = 0)
      
    • 複雜頭寫入

      @ExcelProperty({"主標題", "字元串標題"})
      
  • DateTimeFormat:定義時間格式

    @DateTimeFormat("yyyy年MM月dd日HH時mm分ss秒")
    @ExcelProperty("日期標題")
    private Date date;
    
  • NumberFormat:定義數字格式

    @NumberFormat("#.##%")	// 用百分比表示
    @ExcelProperty(value = "數字標題")
    private Double doubleData;
    
  • ColumnWidth:列寬

  • ContentRowHeight:行高

  • HeadRowHeight:表頭行高(寫在類名上)

  • ContentLoopMerge:合併單元格

    // 這一列 每隔2行 合併單元格
    @ContentLoopMerge(eachRow = 2)
    @ExcelProperty("字元串標題")
    private String string;
    
  • ExcelIgnore:忽略這個欄位

2.將數據存儲到實體類中

    private List<DemoData> data() {
        List<DemoData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字元串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

3.數據寫入 Excel

方法1:在數據量不大的情況下可以使用(5000以內,具體也要看實際情況),數據量大參照 重覆多次寫入

    @Test
    public void simpleWrite() {

        // 創建一個文件的絕對路徑
        String fileName = "D:\\demo1.xlsx";

        // write(): 參數一:文件路徑    參數二:要寫的數據對應的實體位元組碼
        // sheet(): excel表格名稱
        // doWrite(): 要寫的數據,是一個集合
        EasyExcel.write(fileName, ExcelEntity.class).sheet("模板").doWrite(() -> {
            // 分頁查詢數據
            return data();
        });
    }

方法2:

    @Test
    public void simpleWrite() {
        
        String fileName = "D:\\demo1.xlsx";
        // 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後文件流會自動關閉
        // 如果這裡想使用03 則 傳入excelType參數即可
        EasyExcel.write(fileName, ExcelEntity.class).sheet("模板").doWrite(data());
    }

方法3:

    @Test
    public void simpleWrite3() {
        String fileName = "D:\\demo1.xlsx";
        // 這裡 需要指定寫用哪個class去寫
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelEntity.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(data(), writeSheet);
        }
    }

4.忽略或指定導出

忽略

        // 根據用戶傳入欄位 假設我們要忽略 date
        Set<String> excludeColumnFiledNames = new HashSet<String>();
        excludeColumnFiledNames.add("date");
        // 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後文件流會自動關閉
        EasyExcel.write(fileName, ExcelEntity.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板")
                .doWrite(data());

指定導出

        // 根據用戶傳入欄位 假設我們只要導出 date
        Set<String> includeColumnFiledNames = new HashSet<String>();
        includeColumnFiledNames.add("date");
        // 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後文件流會自動關閉
        EasyExcel.write(fileName, ExcelEntity.class).includeColumnFiledNames(includeColumnFiledNames).sheet("模板")
                .doWrite(data());

5.在同一 sheet 續寫

    @Test
    public void repeatedWrite() {
        
        String fileName = "D:\\demo1.xlsx";
        // 這裡 需要指定寫用哪個class去寫
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelEntity.class).build()) {
            // 這裡註意 如果同一個sheet只要創建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            // 去調用寫入,這裡我調用了五次,實際使用時根據資料庫分頁的總的頁數來
            for (int i = 0; i < 5; i++) {
                // 分頁去資料庫查詢數據 這裡可以去資料庫查詢每一頁的數據
                List<ExcelEntity> data = data();
                excelWriter.write(data, writeSheet);
            }
        }
    }

6.同一個對象寫到不同的sheet

    @Test
    public void repeatedWrite() {
        
        String fileName = "D:\\demo1.xlsx";
        // 這裡 指定文件
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelEntity.class).build()) {
            // 去調用寫入,這裡我調用了五次,實際使用時根據資料庫分頁的總的頁數來。這裡最終會寫到5個sheet裡面
            for (int i = 0; i < 5; i++) {
                // 每次都要創建writeSheet 這裡註意必須指定sheetNo 而且sheetName必須不一樣
                WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();
                // 分頁去資料庫查詢數據 這裡可以去資料庫查詢每一頁的數據
                List<ExcelEntity> data = data();
                excelWriter.write(data, writeSheet);
            }
        }
    }

7.不同的對象寫到不同的sheet

    @Test
    public void repeatedWrite() {

        String fileName = "D:\\demo1.xlsx";
        // 這裡 指定文件
        try (ExcelWriter excelWriter = EasyExcel.write(fileName).build()) {
            // 去調用寫入,這裡我調用了五次,實際使用時根據資料庫分頁的總的頁數來。這裡最終會寫到5個sheet裡面
            for (int i = 0; i < 5; i++) {
                // 每次都要創建writeSheet 這裡註意必須指定sheetNo 而且sheetName必須不一樣。這裡註意DemoData.class 可以每次都變,我這裡為了方便 所以用的同一個class
                // 實際上可以一直變
                WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).head(ExcelEntity.class).build();
                // 分頁去資料庫查詢數據 這裡可以去資料庫查詢每一頁的數據
                List<ExcelEntity> data = data();
                excelWriter.write(data, writeSheet);
            }
        }
    }

讀 Excel

1.以實體類映射想要讀取的 Excel 數據

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExcelEntity {
    private String string;
    private Date date;
    private Double doubleData;
}

2.持久層

/**
 * 假設這個是你的DAO存儲。當然還要這個類讓spring管理,當然你不用需要存儲,也不需要這個類。
 **/
public class ExcelDAO {
    public void save(List<ExcelEntity> list) {
        // 如果是mybatis,儘量別直接調用多次insert,自己寫一個mapper裡面新增一個方法batchInsert,所有數據一次性插入
    }
}

3.監聽器

// 有個很重要的點 ExcelEntityListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
public class ExcelEntityListener implements ReadListener<ExcelEntity> {

    /**
     * 每隔5條存儲資料庫,實際使用中可以100條,然後清理list ,方便記憶體回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 緩存的數據
     */
    private List<ExcelEntity> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假設這個是一個DAO,當然有業務邏輯這個也可以是一個service。當然如果不用存儲這個對象沒用。
     */
    private ExcelDAO excelDAO;

    public ExcelEntityListener() {
        // 這裡是demo,所以隨便new一個。實際使用如果到了spring,請使用下麵的有參構造函數
        excelDAO = new ExcelDAO();
    }

    /**
     * 如果使用了spring,請使用這個構造方法。每次創建Listener的時候需要把spring管理的類傳進來
     *
     * @param excelDAO
     */
    public ExcelEntityListener(ExcelDAO excelDAO) {
        this.excelDAO = excelDAO;
    }

    /**
     * 這個每一條數據解析都會來調用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(ExcelEntity data, AnalysisContext context) {
        cachedDataList.add(data);
        // 達到BATCH_COUNT了,需要去存儲一次資料庫,防止數據幾萬條數據在記憶體,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存儲完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有數據解析完成了 都會來調用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 這裡也要保存數據,確保最後遺留的數據也存儲到資料庫
        saveData();
    }

    /**
     * 加上存儲資料庫
     */
    private void saveData() {
        excelDAO.save(cachedDataList);
    }
}

4.讀取 Excel 數據

    @Test
    public void simpleRead() {
        // 寫法1:JDK8+ ,不用額外寫一個DemoDataListener
        // since: 3.0.0-beta1
        String fileName = "D:\\damo1.xlsx";
        // 這裡預設每次會讀取100條數據 然後返回過來 直接調用使用數據就行
        // 具體需要返回多少行可以在`PageReadListener`的構造函數設置
        EasyExcel.read(fileName, ExcelEntity.class, new PageReadListener<ExcelEntity>(dataList -> {
            for (ExcelEntity demoData : dataList) {
                log.info("讀取到一條數據{}", JSON.toJSONString(demoData));
            }
        })).sheet().doRead();

        // 寫法2:
        // 匿名內部類 不用額外寫一個DemoDataListener
        fileName = "D:\\damo1.xlsx";
        // 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 文件流會自動關閉
        EasyExcel.read(fileName, ExcelEntity.class, new ReadListener<ExcelEntity>() {
            /**
             * 單次緩存的數據量
             */
            public static final int BATCH_COUNT = 100;
            /**
             *臨時存儲
             */
            private List<ExcelEntity> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(ExcelEntity data, AnalysisContext context) {
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    // 存儲完成清理 list
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }

            /**
             * 加上存儲資料庫
             */
            private void saveData() {
                log.info("{}條數據,開始存儲資料庫!", cachedDataList.size());
                log.info("存儲資料庫成功!");
            }
        }).sheet().doRead();

        // 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
        // 寫法3:
        fileName = "D:\\damo1.xlsx";
        // 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 文件流會自動關閉
        EasyExcel.read(fileName, ExcelEntity.class, new ExcelEntityListener()).sheet().doRead();

        // 寫法4
        fileName = "D:\\damo1.xlsx";
        // 一個文件一個reader
        try (ExcelReader excelReader = EasyExcel.read(fileName, ExcelEntity.class, new ExcelEntityListener()).build()) {
            // 構建一個sheet 這裡可以指定名字或者no
            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            // 讀取一個sheet
            excelReader.read(readSheet);
        }
    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • > 你準備好面試了嗎?這裡有一些面試中可能會問到的問題以及相對應的答案。如果你需要更多的面試經驗和麵試題,關註一下"張飛的豬大數據分享"吧,公眾號會不定時的分享相關的知識和資料。 [TOC] ## 1、 HDFS 中的 block 預設保存幾份? 預設保存3份 ## 2、HDFS 預設 BlockS ...
  • 雖然漢字#起名名字#的資料庫已經有一些,比如7千多漢字起名參考大典ACCESS資料庫、漢字起名中文起名寶寶起名ACCESS資料庫,但是今天發現了一個資料庫,他是在《7千多漢字起名參考大典》的基礎上增加了30萬個男孩女孩的名字實例。非常適合於比如固定了名字的第二個字,取第三個字時一查就有參考。 漢字表 ...
  • 燈謎,即寫在彩燈上面的謎語,又叫“燈虎”。猜燈謎又叫“射燈虎”。謎語來源於民間口謎,後經文人加工成為謎,它在中國源遠流長。春秋戰國時期,出現了“隱語”或“庾辭”。秦漢時則成為一種書面創作。三國時代,猜謎盛行。在宋代出現了燈謎。人們將謎條繫於五彩花燈上,供人猜射。明清時代,猜燈謎在民間十分流行。 按“ ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 面試官:“HTTPS的加密過程你知道麽?” 我:“那肯定知道啊。” 面試官:“那你知道什麼情況下 HTTPS 不安全麽” 我:“這....” 越面覺得自己越菜,繼續努力學習!!! 什麼是中間人攻擊? 中間人攻擊(MITM)在密碼學和電腦 ...
  • # VuePress2.0構建項目文檔系統 參考TerraMours 官網。[https://terramours.site/](https://terramours.site/) 文件結構參考: ![image-20230530170541496](https://www.raokun.top/u ...
  • 當我們在電腦中使用浮點數進行計算時,特別是在使用二進位表示浮點數時,可能會出現舍入誤差。這是由於電腦使用有限的位數來表示浮點數,而某些十進位數無法精確地表示為有限的二進位數。 0.1 和 0.2 都是無限迴圈的二進位數,在轉換為浮點數時並不能完全準確地表示。將它們相加時,可能會出現舍入誤差。因此 ...
  • ## 1. 背景 - 業務背景:CRM系統隨著各業務條線對線索精細化分配的訴求逐漸增加,各個條線的流向規則會越來越複雜,各個條線甚至整個CRM的線索流轉規則急需一種樹形的可視化的圖來表達。 - 技術背景:在開發之前考慮了三種方案,原生canvas、fabric以及G6,三種方案各有優劣勢 |  | ...
  • 上一篇:微服務架構基本原理學習筆記(一) 三、微服務架構 從一個已有的單體架構的應用程式開始進行微服務架構的重構往往是一個不錯的選擇。隨著業務量和功能的增加,我們可以考慮使用微服務架構來擴充應用程式中原有的功能,或者每次添加新功能時,都為其創建一個新的微服務。這比從一開始就選擇使用微服務架構進行設計 ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 1. 講故事 這一期程式故障除了做原理分析,還順帶吐槽一下,熟悉我的朋友都知道我分析dump是免費的,但免費不代表可以濫用我的寶貴時間,我不知道有些人故意惡搞卡死是想幹嘛,不得而知,希望後面類似的事情越來越少吧!廢話不多說,我們來看看是如何被惡搞的。 二:WinDbg 分析 1. 程式是如 ...
  • TCP(Transmission Control Protocol): 特點:面向連接、可靠傳輸、按序交付、流量控制、擁塞控制。 用途:適用於需要高可靠性的數據傳輸,如網頁瀏覽、電子郵件、文件傳輸等。 優勢:數據包順序和完整性有保障,適合需要準確無誤傳輸數據的場景。 舉例:線上購物網站的交易數據傳輸 ...
  • 前面兩篇隨筆介紹了EAV模型(實體-屬性-值)的設計思路和Winform前端對於通用查詢的處理,本篇隨筆繼續深入EAV模型(實體-屬性-值)設計的探討,介紹實體屬性的定義,以及根據不同屬性的定義構建不同的輸入控制項處理,以及列表界面的展示。旨在結合關係型資料庫的熟練使用、性能優勢和MongoDB資料庫... ...
  • IEC60870-5-104 是一種電力自動化系統中常用的通信協議,使用 TCP/IP 協議作為底層通信協議,用於監視和控制電力系統中的各種設備,如變電站、發電機、開關等。 ...
  • 前言:最近幾天有好幾個小伙伴玩WPF,遇到不同頁面,不知道要怎麼傳遞消息。於是,我今天就來演示一個事件聚合器的玩法,採用prism框架來實現。作為福利,內容附帶了主頁面打開對話框時候直接通過參數傳遞消息的一個小例子,具體請自行圍觀。 以下內容,創建wpf項目以及引用prism和實現依賴註入等細節,可 ...
  • 在這篇文章中,我們介紹瞭如何利用大型語言模型為情人節營造難忘的氛圍。通過上傳圖片併進行風格轉化,我們可以為對方呈現一幅獨特的作品,增添浪漫的色彩。同時,藉助搜索功能,我們能夠輕鬆獲取與情人節相關的信息,為策劃活動提供更多靈感和建議。 ...
  • 正文 晚上跳舞回來,在便利店照例買根冰淇淋吃。看到店裡的老闆娘在訓她孩子。言辭依稀可以聽見考上好初中之類。 當時一個臨時起意,打算買兩根冰淇淋,塞一根到他手上,說一句:“我小時候也老被罵,沒什麼。” 然後跑掉。但是在冰櫃里翻了半天,都沒找到自己想吃的那種。與此同時,聽到他媽媽聲色俱厲地說:“你知道我小時 ...
  • strcpy和memcpy 目錄strcpy和memcpy 複製內容: strcpy:專門用於複製字元串,它會一直複製直到遇到源字元串中的'\0'結束符。這意味著如果源字元串長度超過了目標緩衝區的大小(不包括'\0'),就會發生緩衝區溢出,這是一個常見的安全隱患。 memcpy:可以複製任意內容,如 ...
  • 本文介紹在Visual Studio中,通過屬性表,使得一個新建解決方案中的項目可以快速配置已有解決方案的項目中各類已編譯好的C++第三方庫的方法~ ...
  • 將多個第三方包封裝成一個項目後,如果你的目的是讓其他開發人員可以直接引用這些依賴,一般來說有兩種常見的方式: 打成JAR包:將封裝好的項目編譯打包成JAR文件,其他開發人員可以將這個JAR文件添加到他們的項目中,併在項目的構建工具(比如Maven)中配置該JAR作為依賴。這樣做的好處是簡單直接,其他 ...