問題復現
工作中,項目里的導入功能采用了poi讀取然后進行業務操作,在導入50M文件時發生了OOM報錯信息,以下是本地復現的錯誤信息(由於環境不一樣,本地導入14M的文件就已出現錯誤)
究其原因
項目中使用WorkBook這個類處理文件,這會先把文件中cell讀到內存當中,當數據量比較大的時候就會產生java.lang.OutOfMemoryError: Java heap space錯誤。
看一下導入過程中內存的消耗情況
可以看出,導入過程內存消耗越來越大,直至拋出異常
easyExcel要去解決的問題
Excel讀寫時候內存溢出
雖然POI是目前使用最多的用來做excel解析的框架,但這個框架並不那么完美。大部分使用POI都是使用他的userModel模式。userModel的好處是上手容易使用簡單,隨便拷貝個代碼跑一下,剩下就是寫業務轉換了,雖然轉換也要寫上百行代碼,相對比較好理解。然而userModel模式最大的問題是在於非常大的內存消耗,一個幾兆的文件解析要用掉上百兆的內存。現在很多應用采用這種模式,之所以還正常在跑一定是並發不大,並發上來后一定會OOM或者頻繁的full gc。
其他開源框架使用復雜
對POI有過深入了解的估計才知道原來POI還有SAX模式。但SAX模式相對比較復雜,excel有03和07兩種版本,兩個版本數據存儲方式截然不同,sax解析方式也各不一樣。想要了解清楚這兩種解析方式,才去寫代碼測試,估計兩天時間是需要的。再加上即使解析完,要轉換到自己業務模型還要很多繁瑣的代碼。總體下來感覺至少需要三天,由於代碼復雜,后續維護成本巨大。
其他開源框架存在一些BUG修復不及時
由於我們的系統大多數都是大並發的情況下運行的,在大並發情況下,我們會發現poi存在一些bug,如果讓POI團隊修復估計遙遙無期了。所以我們在easyexcel對這些bug做了規避。 如下一段報錯就是在大並發情況下poi拋的一個異常。
Caused by: java.io.IOException: Could not create temporary directory '/home/admin/dio2o/.default/temp/poifiles'
at org.apache.poi.util.DefaultTempFileCreationStrategy.createTempDirectory(DefaultTempFileCreationStrategy.java:93) ~[poi-3.15.jar:3.15]
at org.apache.poi.util.DefaultTempFileCreationStrategy.createPOIFilesDirectory(DefaultTempFileCreationStrategy.java:82) ~[poi-3.15.jar:3.15]
報錯地方poi源碼如下
private void createTempDirectory(File directory) throws IOException {
if (!(directory.exists() || directory.mkdirs()) || !directory.isDirectory()) {
throw new IOException("Could not create temporary directory '" + directory + "'");
}
}
仔細看代碼容易明白如果在並發情況下,如果2個線程同時判斷directory.exists()都 為false,但執行directory.mkdirs()如果一些線程優先執行完,另外一個線程就會返回false。最終 throw new IOException("Could not create temporary directory '" + directory + "'")。針對這個問題easyexcel在寫文件時候首先創建了該臨時目錄,避免poi在並發創建時候引起不該有的報錯。
核心原理
easyExcel是阿里的一個開源項目,可以解決內存消耗大的問題,
寫有大量數據的xlsx文件時,POI為我們提供了SXSSFWorkBook類來處理,這個類的處理機制是當內存中的數據條數達到一個極限數量的時候就flush這部分數據,再依次處理余下的數據,這個在大多數場景能夠滿足需求。 讀有大量數據的文件時,使用WorkBook處理就不行了,因為POI對文件是先將文件中的cell讀入內存,生成一個樹的結構(針對Excel中的每個sheet,使用TreeMap存儲sheet中的行)。如果數據量比較大,則同樣會產生java.lang.OutOfMemoryError: Java heap space錯誤。POI官方推薦使用“XSSF and SAX(event API)”方式來解決。 分析清楚POI后要解決OOM有3個關鍵。
1.文件解壓和讀取的方式是通過文件的形式,不再是poi的內存形式
2、避免將全部全部數據一次加載到內存
采用sax模式一行一行解析,並將一行的解析結果以觀察者的模式通知處理。
3、拋棄不重要的數據
Excel解析時候會包含樣式,字體,寬度等數據,但這些數據是我們不關心的,如果將這部分數據拋棄可以大大降低內存使用。Excel中數據如下Style占了相當大的空間。
參考官網介紹:https://github.com/alibaba/easyexcel/blob/master/abouteasyexcel.md