EasyExcel:快速讀寫Excel的工具類
項目介紹
?EasyExcel是一個基于Java的、快速、簡潔、解決大文件內存溢出的Excel處理工具。
他能讓你在不用考慮性能、內存的等因素的情況下,快速完成Excel的讀、寫等功能。
pom地址
?
<!--exel-->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>4.0.3</version>
</dependency>
快速入門
簡單讀
讀取excel的操作,主要如下:
- 創建對應數據對象(映射表格中的列)
- 創建一個xxxListener類(可以使用匿名內部類替代)
- 創建輸入流(或者其他io方式)
- 調用EasyExcel方法進行讀取
test.xlsx,使用此文件進行讀取,放入resources
下使用
步驟一:創建數據對象
舉例:DemoData
?
@Data
public class DemoData {private Long id;private String nickName;private Double score;
}
- ?
@Data
是Lombok[^1]中的方法,可以快速生成setter和getter以及toString等
步驟二:創建Listener
舉例:DemoDataListener
?
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {/*** 每隔5條存儲數據庫,實際使用中可以100條,然后清理list ,方便內存回收*/private static final int BATCH_COUNT = 100;/*** 緩存的數據*/private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);/*** 每讀一條數據解析就會調用此方法* @param demoData* @param analysisContext*/@Overridepublic void invoke(DemoData demoData, AnalysisContext analysisContext) {Gson gson = new Gson();log.info("解析到一條數據:{}", gson.toJson(demoData));cachedDataList.add(demoData);// 達到BATCH_COUNT了,需要去存儲一次數據庫,防止數據幾萬條數據在內存,容易OOMif (cachedDataList.size() >= BATCH_COUNT) {saveData();// 存儲完成清理listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {// 這里也要保存數據,確保最后遺留的數據也存儲到數據庫saveData();log.info("所有數據解析完成!");}/*** 保存數據到數據庫*/private void saveData() {log.info("{}條數據,開始存儲數據庫!", cachedDataList.size());log.info("存儲數據庫成功!");}}
- Listener可以對解析的數據進行更高自由度的操作:如
寫入數據到數據庫
?
步驟三:創建輸入流
接下來的代碼和步驟四都是一個代碼中的,此階段為調用。
創建輸入流代碼塊:
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test.xlsx")) {...
}
catch (Exception e) {e.printStackTrace();
}
?
步驟四:調用方法
?
tips
:這里舉例的是其中一種調用方法,詳情請參考:完整讀取代碼案例
?
EasyExcel.read(inputStream, DemoData.class, new PageReadListener<DemoData>(dataList -> {dataList.forEach(data -> {log.info("讀取到數據:{}", data);});
})).sheet().doRead();
完整讀取代碼案例
?ReadTest.java
:
@Slf4j
public class ReadTest {/*** 方法1:簡單讀* 1. 創建excel對應的對象 參照* 2. 由于默認一行行讀取excel,所以需要創建excel一行一行的回調監聽器* 3. 直接讀即可*/@Testpublic void simpleRead() {try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test.xlsx")) {EasyExcel.read(inputStream, DemoData.class, new PageReadListener<DemoData>(dataList -> {dataList.forEach(data -> {log.info("讀取到數據:{}", data);});})).sheet().doRead();} catch (Exception e) {e.printStackTrace();}}/*** 方法2:簡單讀(匿名內部類)* 優化點:更多自定義空間,如:讀取過程中添加存儲到數據庫* 1. 創建excel對應的對象 參照* 2. 由于默認一行行讀取excel,所以需要創建excel一行一行的回調監聽器* 3. 直接讀即可*/@Testpublic void simpleRead2() {try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test.xlsx")) {EasyExcel.read(inputStream, DemoData.class, new ReadListener<DemoData>() {/*** 單次緩存數據量*/private static final int BATCH_COUNT = 100;/*** 臨時存儲*/private List<DemoData> cacheDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Overridepublic void invoke(DemoData demoData, AnalysisContext analysisContext) {cacheDataList.add(demoData);log.info("讀取到數據:{}", demoData);if (cacheDataList.size() >= BATCH_COUNT) {saveData();// 存儲完成清理 listcacheDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {saveData();}/*** 模擬存儲數據庫*/private void saveData() {log.info("{}條數據, 開始存儲數據庫!", cacheDataList.size());log.info("數據庫存儲成功!");}}).sheet().doRead();} catch (IOException e) {log.error("IOException: {}", e.getMessage());}}/*** 其余兩個最簡單的寫法*/@Testpublic void simpleRead3() {try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test.xlsx")) {// 寫法1// 由于流使用后會自動關閉,所以寫法1和2要分開運行
// EasyExcel.read(inputStream, DemoData.class, new DemoDataListener()).sheet().doRead();// 寫法2try (ExcelReader excelReader = EasyExcel.read(inputStream, DemoData.class, new DemoDataListener()).build()) {// 構建一個sheet 可以指定明知或者sheetNoReadSheet sheet = EasyExcel.readSheet(0).build();// 讀取excelReader.read(sheet);}} catch (IOException e) {log.error("IOException: {}", e.getMessage());}}}
?
指定索引或列名讀取
和簡單讀差不多,主要修改在
Data
上,需要對映射的屬性使用@ExcelProperty
進行配置。
這里使用表格:test1.xlsx
步驟一:創建數據對象
?IndexOrNameData
:
@Data
public class IndexOrNameData {/*** 強制讀取第三個* 一般不建議 index 和 name 同時用*/@ExcelProperty(index = 2)private Double doubleData;/*** 用名字去匹配,這里需要注意,如果名字重復,會只讀取第一個** @ExcelProperty("字符串標題")*/@ExcelProperty("字符串標題")private String string;@ExcelProperty("日期標題")private Date date;}
?
步驟二:創建Listener
?IndexOrNameDataListener
:
@Slf4j
public class IndexOrNameDataListener extends AnalysisEventListener<IndexOrNameData> {/*** 每隔5條存儲數據庫,實際使用中可以100條,然后清理list ,方便內存回收*/private static final int BATCH_COUNT = 5;Gson gson = new Gson();private List<IndexOrNameData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Overridepublic void invoke(IndexOrNameData data, AnalysisContext context) {log.info("解析到一條數據:{}", gson.toJson(data));cachedDataList.add(data);if (cachedDataList.size() >= BATCH_COUNT) {saveData();cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {saveData();log.info("所有數據解析完成!");}/*** 加上存儲數據庫*/private void saveData() {log.info("{}條數據,開始存儲數據庫!", cachedDataList.size());log.info("存儲數據庫成功!");}
}
步驟三:創建輸入流以及調用
?
/**
* 指定列的下標或列名* 1. 創建excel對應的實體對象,并使用{@link ExcelProperty}注解* 2. 由于默認一行行的讀取excel,所以需要創建excel一行一行的回調監聽器* 3. 直接讀即可*/
@Test
public void indexOrNameRead() {try (InputStream in = getClass().getClassLoader().getResourceAsStream("test1.xlsx")) {EasyExcel.read(in, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();} catch (IOException e) {log.error("IOException: {}", e.getMessage());}
}
?