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
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...