說一下你了解的POI和EasyExcel
POI(Poor Obfuscation Implementation):它是 Apache 軟件基金會的一個開源項目,為 Java 程序提供了讀寫 Microsoft Office 格式文件的功能,支持如 Excel、Word、PowerPoint 等多種文件格式,是 Java 操作 Office 文檔的基礎工具,發展歷史悠久,功能強大且全面
EasyExcel:是阿里巴巴開源的一個基于 POI 進行二次封裝的輕量級 Excel 處理框架,主要專注于 Excel 文件的讀寫操作,簡化了 POI 的操作流程
EasyExcel對比POI
功能豐富度
POI:POI 是一個底層的 Java 操作 Excel 的工具包,在進行讀寫操作時,需要手動處理文件流、解析器、工作表、行和單元格等多個元素
以讀取文件為例,需要創建工作簿對象、獲取工作表、遍歷行和單元格等,代碼量較多且邏輯復雜。
對于復雜的操作,如設置單元格樣式、合并單元格等,還需要編寫更多的代碼來實現。這使得使用 POI 進行開發時,代碼的復雜度顯著增加
EasyExcel:
EasyExcel 對常見的 Excel 讀寫場景進行了高度封裝,提供了極為簡潔的 API。在讀取 Excel 時,只需定義與表格列對應的實體類和監聽器
內存占用
EasyExcel 采用基于事件驅動的讀寫模式,在讀寫大數據量的 Excel 文件時,逐行處理數據,不需要將整個文件加載到內存中。這使得它在處理大規模數據時,內存占用非常低,能夠有效避免內存溢出的問題。即使處理包含數百萬行數據的 Excel 文件,也能穩定運行
POI 的用戶模型 API(如 HSSF
和 XSSF
)在處理 Excel 文件時,會將整個文件加載到內存中,當文件數據量較大時,會占用大量的內存資源,容易導致內存溢出。雖然 POI 也提供了事件模型 API(如 SAX
解析器)來處理大數據量文件,但使用起來相對復雜,需要開發人員具備較高的技術水平
讀寫性能
POI:POI 的用戶模型 API 在處理大數據量文件時,由于需要將整個文件加載到內存中,會導致讀寫效率降低。而事件模型 API 雖然在處理大數據量時性能較好,但需要手動處理事件,開發難度較大,并且在處理一些復雜操作時,效率也會受到一定影響
EasyExcel:由于采用了事件驅動的方式,EasyExcel 在讀寫數據時,數據處理和文件操作是同步進行的,減少了中間環節,提高了讀寫效率。特別是在處理大數據量文件時,其逐行處理的方式能夠更快地完成讀寫操作
在處理大數據量的 Excel 文件時,EasyExcel 采用了基于事件驅動的讀寫模式,逐行處理數據,避免了將整個文件加載到內存中,從而大大降低了內存占用。例如,當需要讀取一個包含數百萬行數據的 Excel 文件時,使用 POI 的用戶模型 API 可能會導致內存溢出,而 EasyExcel 可以穩定地處理這些數據
事件驅動為什么快+安全?
EasyExcel 通過 SAX 事件驅動 + 流式讀寫,實現了:
? 超低內存占用(恒定內存,與文件大小無關)。
? 高效處理大文件(GB 級 Excel 輕松應對)。
? 避免 OOM(徹底解決 POI 的內存瓶頸)
為什么說EasyExcel的OOM風險小
EasyExcel不需要將整個文件加載到內存中,poi需要加載整個文件?
1. 傳統 POI 的 DOM 模式:全量加載
DOM(Document Object Model):
POI 將整個 Excel 文件解析為一個樹形結構對象,全部加載到內存中。
類似于把一本書全部復印到腦子里,再逐頁閱讀
2. EasyExcel 的 SAX 模式:流式逐行處理
SAX(Simple API for XML):
EasyExcel 像流水線一樣逐行掃描 Excel 文件,觸發事件回調處理數據。
類似于用掃描儀逐頁掃描一本書,讀一頁處理一頁,然后立刻扔掉
EasyExcel 的 SAX 模式 通過事件驅動 + 即時丟棄數據(即時刷盤),實現了按需加載,而 POI 的 DOM 模式 為支持復雜功能必須全量加載
EasyExcel如何避免寫入OOM?
POI必須是全量寫入,這也是它的內存瓶頸
而EasyExcel 默認每處理 2000 行(可配置)就將數據從內存刷入磁盤文件流,即時釋放內存。
內存占用 ≈ 單批次數據大小(而非全量數據)。例如寫入sheet1然后釋放sheet1,再寫入sheet2,而不用sheet1,sheet2必須一起寫入,分sheet頁寫可以實現資源及時釋放
EasyExcel為啥不維護了?對此你怎么看?
核心功能已成熟,暫無重大特性新增需求。以修復 Bug 和兼容性更新為主,而非頻繁發布新功能
開發者資源轉移,阿里內部可能將資源投入其他更高優先級項目(如 Spring Cloud Alibaba),開源團隊依賴社區貢獻,但核心開發者時間有限
面試回答思路引導
面試回答
poi是apache的開源項目,功能非常豐富,而EasyExcel是基于poi的二次簡化封裝,簡化了poi的操作,方便使用
主要讓寫入和寫出更加簡單,沒必要去操作很多的細節,例如不需要手動編寫輸入輸出流,手動關閉輸入輸入流的資源,編寫解析器等
EasyExcel只需要只需定義與表格列對應的實體類和監聽器
EasyExcel主要解決的poi的內存瓶頸和讀取效率問題
poi使用的是傳統的全量加載,而EasyExcel是流失加載,也就是事件驅動+及時刷盤
poi讀取的時候,必須要把整個文件加載到內存中讀取
poi寫入的時候必須一次性全量寫入
而EasyExcel讀取的時候是流式讀取,逐行掃描,讀取完的數據從內存中扔掉,不占用內存
EasyExcel寫入的時候是流式寫入,我們分sheet頁寫入,這個sheet頁寫入完就及時釋放資源,實現及時刷盤不占用內存
這是他們的區別,也是基于poi的重點優化,poi因為是全量讀取和寫入邏輯所以oom的概率會非常大
EasyExcel是寫完sheet頁就釋放不會一直占用內存,所以oom的概率小,但這并不說明不會出現oom問題了,使用EasyExcel的時候還是需要嚴格控制sheet頁的,因為sheet頁是存到一個List<>中的,如果sheet頁過大仍然會導致oom問題
常用的方法
EasyExcel.read() 讀
EasyExcel.write() 寫
WriteSheet sheet = EasyExcel.writerSheet("批次").build() 構造sheet頁
ExcelWriter writer = EasyExcel.write(filePath).build() 構造用來寫入的ExcelWriter
writer.write(excels, sheet) 把Sheet頁放到ExcelWriter里面,然后寫入我們傳入的內容excels列表
@Testvoid allData(){List<Excel> excels = excelMapper.selectAllOrderById();String filePath = "C:\\Users\\ziJian.zheng\\IdeaProjects\\Kira-Test\\src\\main\\resources\\templates\\test.xlsx";WriteSheet sheet = EasyExcel.writerSheet("批次" ).build();//用來寫入的ExcelWriterExcelWriter writer = EasyExcel.write(filePath).build();writer.write(excels, sheet);}
常見的注解
@ExcelProperty(value = "用戶姓名",index = 0)
效果:index屬性可以指定當前字段對應excel中的哪一列,可以根據列名value去匹配,也可以不寫
如果不使用@ExcelProperty注解,成員變量從上到下的順序,對應表格中從左到右的順序
@ExcelIgnore
讀寫的時候會忽略該字段
@DateTimeFormat 日期轉換
日期格式轉換:將 Excel 中的日期格式數據(如 2023-01-01
)與 Java 字段(String
或 Date
)自動轉換
@DateTimeFormat("yyyy-MM-dd") // 格式化為"2023-01-01"
private String createDate;
@NumberFormat 數字格式轉換
將 Excel 中的數字(如 1,000.50
)與 Java 字段(String
或數值類型)自動轉換
@NumberFormat("#,##0.00") // 格式化為"1,000.50"
private String price;
@ExcelIgnoreUnannotated
制類字段的默認行為:標注在類上,決定未加 @ExcelProperty
的字段是否參與讀寫。
不標注該注解:所有字段(無論是否有 @ExcelProperty
)都會參與讀寫。
標注該注解:只有顯式標注 @ExcelProperty
的字段參與讀寫