EasyExcel詳解

文章目錄

    • 一、easyExcel
      • 1.什么是easyExcel
      • 2.easyExcel示例demo
      • 3.easyExcel read的底層邏輯
      • ~~4.easyExcel write的底層邏輯~~
    • 二、FastExcel
      • 1.為什么更換為fastExcel
      • 2.fastExcel新功能

一、easyExcel

1.什么是easyExcel

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

通俗解釋就是說:一個基于poi的excel簡化開發包,性能比poi要好,且易于使用

官方文檔地址
源碼地址

2.easyExcel示例demo

官方文檔非常全面,本無需寫一個demo來記錄。本demo旨在展示easyExcel的讀寫基礎用法、自定義類型轉換、自定義單元格格式及excel空白行處理等,可以理解為將常用的情況記錄下來,省去看官方文檔的時間。

## PersonVO.class,代碼中的Person.classPersonVO.class的區別為沒有ifOffer字段,為了展示而做了區分
## Person.class是用來讀excel的,PersonVO.class用來寫excel
@Data
public class PersonVo {@ExcelProperty("名稱")private String name;@ExcelProperty("性別")private String gender;@ExcelProperty("年齡")private Integer age;@ExcelProperty("信息")private String info;@ExcelProperty("評分")private Float score;// OfferEnumConverter為自定義的Converter,用來做OfferEnum和String的映射@ExcelProperty(value = "是否錄用", converter = OfferEnumConverter.class)private OfferEnum ifOffer;
}## excel讀及寫部分,如果read時使用PersonVo.class映射表頭
## 則可以在CustomPageReadListener.class的invoke方法中,做對person.ifOffer的賦值File file = new File("D:\\develop\\work\\test.xlsx");
try (InputStream is = Files.newInputStream(file.toPath())) {// 讀取數據List<PersonVo> excelDatas = new ArrayList<>();EasyExcel.read(is, Person.class, new CustomPageReadListener<Person>(dataList -> {if (CollectionUtils.isEmpty(dataList)) {return;}dataList.forEach(data -> {PersonVo personVo = new PersonVo();BeanUtils.copyProperties(data, personVo);excelDatas.add(personVo);});})).sheet().doReadSync();// 為了實現自定義表格樣式,根據ifOffer來決定行顏色Map<Integer, Short> cellColorType = new HashMap<>();for (int i = 0; i < excelDatas.size(); i++) {PersonVo person = excelDatas.get(i);if (person.getScore() > 3) {person.setIfOffer(OfferEnum.OFFER);cellColorType.put(i + 1, IndexedColors.GREEN.getIndex());} else if (person.getScore() < 2) {person.setIfOffer(OfferEnum.REFUSE);cellColorType.put(i + 1, IndexedColors.RED.getIndex());} else {person.setIfOffer(OfferEnum.WAIT);cellColorType.put(i + 1, IndexedColors.YELLOW.getIndex());}}EasyExcel.write("D:\\develop\\work\\test1.xlsx", PersonVo.class).registerWriteHandler(new CustomCellWriteHandler(cellColorType)).sheet("測試").doWrite(excelDatas);
} catch (IOException e) {throw new RuntimeException(e);
}

demo中用到了自定義類型轉換OfferEnumConverter、自定義excel讀取監聽器CustomPageReadListener、自定義WriteHandler CustomCellWriteHandler,是實際開發中這三個是最常用的工具

  1. OfferEnumConverter: String <–> Enum轉換器,實現supportJavaTypeKey及supportExcelTypeKey是為了在Easy.registerConverter()注冊通用轉換器也可以使用
## OfferEnumConverter.class
public class OfferEnumConverter implements Converter<OfferEnum> {@Overridepublic Class<OfferEnum> supportJavaTypeKey() {return OfferEnum.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic OfferEnum convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {return OfferEnum.valueOf(cellData.getStringValue());}@Overridepublic WriteCellData<?> convertToExcelData(OfferEnum value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {if(Objects.isNull(value)) {return new WriteCellData<>("");} else {return new WriteCellData<>(value.getValue());}}
}## OfferEnum.class 錄用標記枚舉
@Getter
public enum OfferEnum {OFFER("y", "錄用"),REFUSE("n", "不錄用"),WAIT("wait", "待定");;private final String value;private final String desc;OfferEnum(String value, String desc) {this.value = value;this.desc = desc;}public static OfferEnum getByValue(String value) {for (OfferEnum offerEnum : OfferEnum.values()) {if (offerEnum.value.equals(value)) {return offerEnum;}}return WAIT;}
}
  1. CustomPageReadListener: 監聽器是在讀取完一行數據后被調用的,invoke中接收到的是一行的數據。這里做了處理空行的操作,雖然EasyExcel默認情況下會配置ignoreEmptyRow為true,但是如果行內某個單元格無數據但有單元格式,會被EasyExcel認為非空行,因此對空行嚴謹的項目需要在這里處理一下空行。
public class CustomPageReadListener<T> extends PageReadListener<T> {public CustomPageReadListener(Consumer<List<T>> consumer) {super(consumer);}@Overridepublic void invoke(T data, AnalysisContext context) {// 處理空行if (isNullLine(data)) {return;}// 特殊字段賦值及處理(如:dateStr賦值給date)flushData(data);// 處理數據轉換異常super.invoke(data, context);}private void flushData(T data) {}private boolean isNullLine(T data) {System.err.println(JSON.toJSONString(data));// 獲取data每個字段,反射判斷是不是都為空或空字符串for (Field field : data.getClass().getDeclaredFields()) {field.setAccessible(true);try {Object value = field.get(data);if (value instanceof String) {if (!StringUtils.isEmpty(value)) {return false;}} else {if (Objects.nonNull(value)) {return false;}}} catch (IllegalAccessException e) {return false;}}return true;}
}
  1. CustomCellWriteHandler: 將內存中的數據寫入excel時,需要做一些特殊處理時(如:脫敏處理、添加單元格樣式、合并單元格等),可以通過實現WriteHandler來實現功能,demo中只有添加單元格樣式,官方文檔中有很全面的各種案例用法
public class CustomCellWriteHandler implements CellWriteHandler {private final Map<Integer, Short> cellColorType;public CustomCellWriteHandler(Map<Integer, Short> cellColorType) {if(Objects.isNull(cellColorType)) {cellColorType = new HashMap<>();}this.cellColorType = cellColorType;}@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {// 表頭樣式不變if (BooleanUtils.isNotTrue(context.getHead())) {int rowIndex = context.getRowIndex();Short colorIndex = cellColorType.get(rowIndex);if(Objects.nonNull(colorIndex)) {WriteCellData<?> cellData = context.getFirstCellData();// 這里需要去cellData 獲取樣式// 很重要的一個原因是 WriteCellStyle 和 dataFormatData綁定的 簡單的說 比如你加了 DateTimeFormat// ,已經將writeCellStyle里面的dataFormatData 改了 如果你自己new了一個WriteCellStyle,可能注解的樣式就失效了// 然后 getOrCreateStyle 用于返回一個樣式,如果為空,則創建一個后返回WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();writeCellStyle.setFillForegroundColor(colorIndex);// 這里需要指定 FillPatternType 為FillPatternType.SOLID_FOREGROUNDwriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);}}}
}

本案例使用的test.excel數據及導出后的效果參照下圖:
Excel測試數據
WriteExcel結果

3.easyExcel read的底層邏輯

通過ExcelAnalyser來配置excel解析執行器

  • 通過FileMagic來讀取文件開頭幾個字節的魔數,以確定文件的類型。為了兼容CSV文件,通過File方式readExcel的時候,通過判斷文件的后綴名稱是否為.csv來判斷是否為CSV文件
  • 設置read上下文:解析表頭,加載readListener、Converter(預定義的Converter和通過registerConverter注冊的Converter)、設置忽略空行(如果空行中有表格樣式,則無法忽略)及readCache
  • 設置read執行器:選擇合適的執行器,并加載所有的sheet。這里加載了所有的sheet,在read的時候會根據條件選擇要讀取的sheet

通過ExcelAnalyser.analysis來解析excel

  • 從xlsx視角出發的,xls和csv這里不做展示
  • XlsxSaxAnalyser.parseXmlSource()中使用SAXParserFactory來解析 Excel 文件底層 XML 結構。SAXParserFactory基于 SAX(Simple API for XML)事件驅動模型實現高效的大文件流式解析,避免內存溢出(OOM)
  • XlsxRowHandler重寫了startElement來實現對每一行每一個單元格的讀取。當所有XlsxTagHandler執行完后,開始endElement進行cell類型的轉換等,最終交給AnalysisEventProcessor.endRow來處理數據,并調用ReadListener監聽器來對數據做處理(如PageReadListener來緩存數據)
  • EasyExcel有四個解析excel的入口,分別為
    • .sheet().doRead() – sheet中不加參數,則默認取sheetNo為0的sheet,doRead中進行解析excel
    • .sheet().doReadSync() – 相對doRead(),注冊了一個新的Listener用來緩存數據,讀取excel結束后直接從Listner中讀取數據并return
    • doReadAll() – 顧名思義,讀取所有的sheet(),并映射到同一個實體list中,適合同類型分頁數據
    • .doReadAllSync() – 同上
  • 讀取excel的關鍵為SAXParserFactory和ReadCache,具體邏輯可以自己閱讀源碼,或使用AI工具輔助閱讀

4.easyExcel write的底層邏輯


二、FastExcel

文本采用的fastExcel版本為1.0.0,當前時間最新版本為1.2.0

目前FastExcel官網已掛,僅有開源源碼地址

1.為什么更換為fastExcel

  • 2024年8月阿里已宣布停止更新easyExcel,同時原作者宣布新開發fastExcel,支持所有easyExcel的功能,因此原easyExcel用戶可以最低成本過度到fastExcel
  • fastExcel通過對底層算法的優化和內存管理的改進,能更高效的處理大規模的excel數據,大幅降低內存消耗和處理時間
  • 新功能:讀取excel指定行數,excel轉pdf(注意:僅僅是將excel文件轉為pdf文件,且在1.1.0版本中已經移除此功能,謹慎使用

2.fastExcel新功能

## fastExcel中既可以用FastExcel.class,也可以用EasyExcel.class,除了1.0.0版本外,倆完全一樣
## .numRows()即讀取excel指定行數,.numRows(10)即從表頭開始讀10,上文中的案例,就只會讀到9條數據
FastExcel.read(is, Person.class, new PageReadListener<Person>(dataList -> {if (CollectionUtils.isEmpty(dataList)) {return;}dataList.forEach(data -> {PersonVo personVo = new PersonVo();BeanUtils.copyProperties(data, personVo);excelDatas.add(personVo);});
})).sheet().numRows(10).doRead();## excel文件轉為pdf文件,謹慎使用
FastExcel.convertToPdf(new File("D:\\develop\\work\\test1.xlsx"), new File("D:\\develop\\work\\test2.pdf"), null, null);

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/906019.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/906019.shtml
英文地址,請注明出處:http://en.pswp.cn/news/906019.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

jvm安全點(三)openjdk17 c++源碼垃圾回收之安全點結束,喚醒線程

1. VMThread::inner_execute() - 觸發安全點?? cpp 復制 void VMThread::inner_execute(VM_Operation* op) { if (op->evaluate_at_safepoint()) { SafepointSynchronize::begin(); // 進入安全點&#xff0c;阻塞所有線程 // ...執行GC等操作... SafepointSynchronize::…

102. 二叉樹的層序遍歷遞歸法:深度優先搜索的巧妙應用

二叉樹的層序遍歷是一種經典的遍歷方式&#xff0c;它要求按層級逐層訪問二叉樹的節點。通常我們會使用隊列來實現層序遍歷&#xff0c;但遞歸法也是一種可行且有趣的思路。本文將深入探討遞歸法解決二叉樹層序遍歷的核心難點&#xff0c;并結合代碼和模擬過程進行詳細講解。 …

首個窗口級無人機配送VLN系統!中科院LogisticsVLN:基于MLLM實現精準投遞

導讀 隨著智能物流需求日益增長&#xff0c;特別是“最后一公里”配送場景的精細化&#xff0c;傳統地面機器人逐漸暴露出適應性差、精度不足等瓶頸。為此&#xff0c;本文提出了LogisticsVLN系統——一個基于多模態大語言模型的無人機視覺語言導航框架&#xff0c;專為窗戶級別…

WPF Datagrid 數據加載和性能

這篇文章并非討論 WPF Datagrid 的性能數據&#xff0c;而只是簡單介紹一下為了使其性能良好&#xff0c;你需要注意哪些方面。我不太想使用性能分析器來展示實際數據&#xff0c;而是盡可能地使用了 Stopwatch 類。這篇文章不會深入探討處理海量數據的技術&#xff0c;例如分頁…

matlab求矩陣的逆、行列式、秩、轉置

inv - 計算矩陣的逆 用途&#xff1a;計算一個可逆矩陣的逆矩陣。 D [1, 2; 3, 4]; % 定義一個2x2矩陣 D_inv inv(D); % 計算矩陣D的逆 disp(D_inv);det - 計算矩陣的行列式 用途&#xff1a;計算方陣的行列式。 E [1, 2; 3, 4]; determinant det(E); % 計算行列式 disp…

ridecore流水線解讀

文章目錄 流水線stage分屬前后端PCpipelineIFIDDPDP 與 SW 中間沒有latchSWCOM 源碼地址 流水線stage分屬前后端 IF -> ID -> DP -> SW -> EX -> COM分類階段說明前端IF指令獲取階段。PC 使用分支預測器&#xff0c;訪問指令存儲器。典型前端操作。前端ID解碼并…

【SpringBoot】關于MP使用中配置了數據庫表前綴的問題

problem 使用MP時&#xff0c;在application.yml配置文件中配置了MP匹配數據庫表中的表名時的前綴作了規定&#xff0c;如下&#xff1a; 那么當我運行時報錯了錯誤&#xff0c;報錯信息如下&#xff1a; 因為我數據庫表的書類表名是book&#xff0c;MP在匹配時使用了表名前…

印度Rummy游戲支付通道申請策略:技巧類游戲的合規與創新

本文為印度支付申請科普文&#xff0c;自去年開始&#xff0c;印度Rummy類游戲申請印度支付都需要擁有AIGF的會員及產品證書。 如需要rummy可以通過AIGF審核的源。碼&#xff0c;或咨詢AIGF的相關內容&#xff0c;可以聯。系老妙。 印度作為全球棋牌類游戲增長最快的市場之一&…

日志與策略模式

什么是設計模式 IT?業 ,為了讓 菜雞們不太拖?佬的后腿, 于是?佬們針對?些經典的常?的場景, 給定了?些對應的解決?案, 這個就是 設計模式 日志認識 計算機中的?志是記錄系統和軟件運?中發?事件的?件&#xff0c;主要作?是監控運?狀態、記錄異常信 息&#xff…

解鎖Ubuntu高效部署!自動安裝配置文件YAML全解析

我們之前介紹了兩種Ubuntu系統的安裝方式&#xff0c;分別對應桌面版&#xff08;準備搞OpenStack了&#xff0c;先裝一臺最新的Ubuntu 23.10&#xff09;和服務器版&#xff08;Ubuntu 22.04 LTS服務器版本安裝演示&#xff09;。但對于有些用戶&#xff0c;因為技術問題&…

關系代數和關系數據庫語言(SQL)

閱讀提示&#xff1a;本篇文章較長&#xff0c;建議從目錄上選取想看的內容。代碼上的話&#xff0c;我習慣用小寫&#xff0c;如果看不習慣建議跳過。有問題歡迎討論&#xff01;&#xff01;&#xff01; 一、基礎概念 1.1數據庫的概念 數據庫(Database)是按照數據結構來組…

EXO 可以將 Mac M4 和 Mac Air 連接起來,并通過 Ollama 運行 DeepSeek 模型

EXO 可以將 Mac M4 和 Mac Air 連接起來&#xff0c;并通過 Ollama 運行 DeepSeek 模型。以下是具體實現方法&#xff1a; 1. EXO 的分布式計算能力 EXO 是一個支持 分布式 AI 計算 的開源框架&#xff0c;能夠將多臺 Mac 設備&#xff08;如 M4 和 Mac Air&#xff09;組合成…

區塊鏈基本理解

文章目錄 前言一、什么是分布式賬本(DLT)二、什么是P2P網絡?二、共識算法三、密碼算法前言 區塊鏈是由一個一個數據塊組成的鏈條,按照時間順序將數據塊逐一鏈接,通過哈希指針鏈接,所有的數據塊共同維護一份分布式賬本(DLT),每個節點(可以理解為一個玩家,一臺計算機)都擁…

Node.js中的洋蔥模型

文章目錄 前言 前言 Node.js中的洋蔥模型是一種中間件執行機制&#xff0c;主要用于處理HTTP請求和響應的流程控制。該模型通過層層包裹的中間件結構&#xff0c;實現請求從外到內穿透、響應從內向外返回的順序執行。以下從核心概念、實現原理、框架差異及實際應用等方面解析&…

UI-TARS Desktop:用自然語言操控電腦,AI 重新定義人機交互

在人工智能技術飛速發展的今天,從文本生成到圖像識別,AI 的能力邊界不斷被打破。而字節跳動近期開源的 UI-TARS Desktop,則將這一技術推向了更復雜的交互場景——通過自然語言直接控制計算機界面,實現了圖形用戶界面(GUI)的智能化自動化。這款工具不僅降低了操作門檻,更…

一個可拖拉實現列表排序的WPF開源控件

從零學習構建一個完整的系統 推薦一個可通過拖拉&#xff0c;來實現列表元素的排序的WPF控件。 項目簡介 gong-wpf-dragdrop是一個開源的.NET項目&#xff0c;用于在WPF應用程序中實現拖放功能&#xff0c;可以讓開發人員快速、簡單的實現拖放的操作功能。 可以在同一控件內…

C語言中字符串函數的詳細講解

C語言提供了豐富的字符串處理函數&#xff0c;這些函數在<string.h>頭文件中聲明。以下是一些常用字符串函數的詳細講解&#xff1a; 字符串拷貝函數 strcpy 功能&#xff1a;將源字符串&#xff08;包括結尾的\0&#xff09;復制到目標字符串。原型&#xff1a;char *s…

可視化數據圖表怎么做?如何實現三維數據可視化?

目錄 一、三維數據可視化的要點 1. 明確數據可視化的目標 2. 篩選與整理數據 3. 選擇合適的圖表類型 4. 運用專業工具制作 5. 優化圖表的展示效果 二、數據可視化圖表怎么做&#xff1f; 1. 理解三維數據的特性 2. 數據處理與三維建模 3. 設置光照與材質效果 4. 添加…

在Linux服務器上部署Jupyter Notebook并實現ssh無密碼遠程訪問

Jupyter notebook版本7.4.2&#xff08;這個版本AI提示我Jupyter7&#xff08;底層是 jupyter_server 2.x&#xff09; 服務器開啟服務 安裝Jupyter notebook 7.4.2成功后&#xff0c;終端輸入 jupyter notebook --generate-config 這將在 ~/.jupyter/ 目錄下生成 jupyter_…

走出 Demo,走向現實:DeepSeek-VL 的多模態工程路線圖

目錄 一、引言&#xff1a;多模態模型的關鍵轉折點 &#xff08;一&#xff09;當前 LMM 的三個關鍵挑戰 1. 數據的真實性不足 2. 模型設計缺乏場景感知 3. 語言能力與視覺能力難以兼顧 &#xff08;二&#xff09;DeepSeek-VL 的根本出發點&#xff1a;以真實任務為錨點…