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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...