最近公司需求解析excel,一開始使用poi做的挺好的,后來直接上了幾十萬條數據的excel文件,內存直接溢出了,網上查到apache poi還提供了專門處理海量數據的方法,使用sax解析,果然用了內存使用率下降,但是不能解析xls文件,只能解析xlsx文件,所以把工具簡單封裝了一下,如果是xls的用傳統方式解析,如果是xlsx的大文件,用sax,這樣靈活一定,詳見md文件
(建議前往github查看最新內容)
https://github.com/hellojamie/ebatis 下載項目源碼
https://gitee.com/hellojamie/ebatis
Ebatis
用於解析excel表格內容到 java bean 目前支持xls、xlsx格式文件 對於大數據量文件自動使用sax方式解析,防止內存溢出
目錄(記得看注意):
- 開始
- 擴展功能
- 注意
開始
因為經常有改動,需要手動打包發布
// Maven導入第三方poi依賴,或者直接把master pull下來發布到本地 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> <version>2.9.1</version> </dependency>
首先你需要創建好你的實體類,假設現在有這樣一個excel表格需要解析
姓名 | 手機號 | 生日 |
---|---|---|
王文娟 | 18888888888 | 1996-01-01 |
大美麗 | 16666666666 | 1996-01-01 |
首先你需要一個實體類 有幾點要求,必須正確封裝,包含get\set方法 屬性上包含必要的Mapping注解,key屬性填入與表格對應的名稱,屬性類型根據需要自己定義
package cc.ebatis.controller; import java.io.File; import cc.ebatis.bean.People; import cc.ebatis.impl.Init; import cc.ebatis.pojo.ActionContext; public class Test { public static void main(String[] args) { File file = new File("c://Users//Pei//Desktop//pei.xlsx"); Init<People> init = new Init<People>(file, People.class, false); System.out.println("init===="+init); ActionContext<People> act = init.start(); System.out.println("act===="+act); } }
package cc.ebatis.bean; import java.util.Date; import cc.ebatis.annotation.Mapping; public class People { @Mapping(key = "姓名") private String name; @Mapping(key = "手機號") private String phone; @Mapping(key = "生日") private Date birth; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } @Override public String toString() { return "People [name=" + name + ", phone=" + phone + ", birth=" + birth + "]"; } }
然后將你的文檔以流的方式加載進來,通過以下代碼開始解析
// Init接受一個InputStream對象,以及一個實體對象 // 調用start開始 // 通過ActionContext獲取需要的信息 // 參數依次為-文件-實體class-是否去重 File file = new File("excel.xlsx"); Init<ExcelPojo> init = new Init<ExcelPojo>(file, ExcelPojo.class, false); ActionContext<ExcelPojo> act = init.start();
(注意:如果要去重的話請重寫實體中的hashCode和equals方法,內部使用set來去重,false表示不去重) ActionContext中包含了所需要的所有信息,信息格式如下,這里以json的形式展示
{ "sheets":[ { "line":5, "sheetName":"Sheet1", "column":6, "correctLine":5, "blankLineSize":0, "errorLineSize":0, "repeatLineSize":0, "info":[ { "date":1331481600000, "name":"王文娟", "phone":"15035214458" }, { "date":1331481600000, "name":"大美麗", "phone":"14555874458" } ], "blankLine":[], "errorLine":[], "repeatLine":[] } ], "fileType":"XLSX", "SheetSize":1, "fileSizeByte":9138, "useSax":true, "distinct":false, "result":true }
屬性名 | 含義 |
---|---|
sheets | sheet數組 |
line | 解析當前sheet一共多少行數據,不算表頭 |
sheetName | sheet的名稱 |
column | 列數 |
info | 實體對象數組,包含實體的列表,也就是行數據 |
correctLine | 實際正確解析出的數量(行數) |
blankLineSize | 空行的數量 |
errorLineSize | 錯誤行的數量,包括正則不通過被刪除的 |
repeatLineSize | 重復行的數量 |
fileType | 文件類型 |
blankLine | 空白行的行號-數組 |
errorLine | 錯誤行的行號-數組 |
repeatLine | 重復行的行號-數組 |
fileSizeByte | 文件大小(字節) |
useSax | 是否使用了sax方式 |
distinct | 是否去重 |
result | 最后是否解析成功,如果中間出錯則是false |
sheetSize | 文件中有幾個sheet |
useSax | 是否使用sax解析,即是否解析的是xlsx文件 |
distinct | 是否去重 |
使用ActionContext的getXXX方法獲取上面的內容
擴展功能
@Mapping注解有三個非必選屬性
-
-
private String phone;
屬性名 | 含義 | 是否必填 |
---|---|---|
key | 填寫與excel文件頭的映射名稱 | 必填 |
rex | 填寫解析內容時使用的正則表達式,如果不符合正則則不賦值 | 非必填 |
delNull | 如果該屬性為null的話,是否刪除整條信息,默認false不刪除 | 非必填 |
length | 填寫提取內容的最大長度,默認不限制 | 非必填 |
@LineNumber注解,獲取當前記錄是第幾行,不算表頭那行
-
-
private Integer line;
@MappingSheetName注解,將sheet名稱作為屬性值添加
-
-
private String type;
注意
- 解析xlsx大文件的時候,POI本身會占據較大內存,例如100W行15列,POI自身將消耗400M+的內存,加上解析出來的內容會大於這個值,以100W為例大概需要700M+內存
- 實體中請使用包裝類,不支持int等類型,請使用Integer
- 列與列之間不能包含表頭為空的列,即不能有空列將信息隔開,如果有,以空列前一列為末尾解析
- excel文件請使用第一行表頭,其余行信息的標准格式,如果有合並單元格情況,可能會解析失敗(可以包含空行和空單元格,會自動過濾,但必須有表頭)
- 實體類的屬性不嚴格要求與列的數量一致,根據需要添加映射注解即可
- 實體類 的屬性和表頭的順序沒有嚴格要求,只要key匹配即可
- 如果需要去重,請重寫實體的hashCode和equals方法,內部使用HashSet方式去重,重寫時請注意
- 最后是否解析成功請以result屬性為准