EasyExcel是一個基於Java的、快速、簡潔、解決大文件記憶體溢出的Excel處理工具。 他能讓你在不用考慮性能、記憶體的等因素的情況下,快速完成Excel的讀、寫等功能。 # 快速入門 導入依賴 ~~~xml com.alibaba easyexcel 3.1.1 ~~~ # 寫 Excel # ...
EasyExcel是一個基於Java的、快速、簡潔、解決大文件記憶體溢出的Excel處理工具。
他能讓你在不用考慮性能、記憶體的等因素的情況下,快速完成Excel的讀、寫等功能。
快速入門
導入依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
寫 Excel
1.以實體類映射想要寫入 Excel 的數據
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExcelEntity {
@ExcelProperty("字元串標題")
private String string;
@ExcelProperty("日期標題")
private Date date;
@ExcelProperty("數字標題")
private Double doubleData;
@ExcelIgnore
private String ignore;
}
註解說明
-
ExcelProperty:定義排版
-
value:字元串標題
-
index:指定數據所在列,從0起算
@ExcelProperty(value = "字元串標題", index = 0)
-
複雜頭寫入
@ExcelProperty({"主標題", "字元串標題"})
-
-
DateTimeFormat:定義時間格式
@DateTimeFormat("yyyy年MM月dd日HH時mm分ss秒") @ExcelProperty("日期標題") private Date date;
-
NumberFormat:定義數字格式
@NumberFormat("#.##%") // 用百分比表示 @ExcelProperty(value = "數字標題") private Double doubleData;
-
ColumnWidth:列寬
-
ContentRowHeight:行高
-
HeadRowHeight:表頭行高(寫在類名上)
-
ContentLoopMerge:合併單元格
// 這一列 每隔2行 合併單元格 @ContentLoopMerge(eachRow = 2) @ExcelProperty("字元串標題") private String string;
-
ExcelIgnore:忽略這個欄位
2.將數據存儲到實體類中
private List<DemoData> data() {
List<DemoData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字元串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
3.數據寫入 Excel
方法1:在數據量不大的情況下可以使用(5000以內,具體也要看實際情況),數據量大參照 重覆多次寫入
@Test
public void simpleWrite() {
// 創建一個文件的絕對路徑
String fileName = "D:\\demo1.xlsx";
// write(): 參數一:文件路徑 參數二:要寫的數據對應的實體位元組碼
// sheet(): excel表格名稱
// doWrite(): 要寫的數據,是一個集合
EasyExcel.write(fileName, ExcelEntity.class).sheet("模板").doWrite(() -> {
// 分頁查詢數據
return data();
});
}
方法2:
@Test
public void simpleWrite() {
String fileName = "D:\\demo1.xlsx";
// 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後文件流會自動關閉
// 如果這裡想使用03 則 傳入excelType參數即可
EasyExcel.write(fileName, ExcelEntity.class).sheet("模板").doWrite(data());
}
方法3:
@Test
public void simpleWrite3() {
String fileName = "D:\\demo1.xlsx";
// 這裡 需要指定寫用哪個class去寫
try (ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelEntity.class).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
excelWriter.write(data(), writeSheet);
}
}
4.忽略或指定導出
忽略
// 根據用戶傳入欄位 假設我們要忽略 date
Set<String> excludeColumnFiledNames = new HashSet<String>();
excludeColumnFiledNames.add("date");
// 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後文件流會自動關閉
EasyExcel.write(fileName, ExcelEntity.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板")
.doWrite(data());
指定導出
// 根據用戶傳入欄位 假設我們只要導出 date
Set<String> includeColumnFiledNames = new HashSet<String>();
includeColumnFiledNames.add("date");
// 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後文件流會自動關閉
EasyExcel.write(fileName, ExcelEntity.class).includeColumnFiledNames(includeColumnFiledNames).sheet("模板")
.doWrite(data());
5.在同一 sheet 續寫
@Test
public void repeatedWrite() {
String fileName = "D:\\demo1.xlsx";
// 這裡 需要指定寫用哪個class去寫
try (ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelEntity.class).build()) {
// 這裡註意 如果同一個sheet只要創建一次
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
// 去調用寫入,這裡我調用了五次,實際使用時根據資料庫分頁的總的頁數來
for (int i = 0; i < 5; i++) {
// 分頁去資料庫查詢數據 這裡可以去資料庫查詢每一頁的數據
List<ExcelEntity> data = data();
excelWriter.write(data, writeSheet);
}
}
}
6.同一個對象寫到不同的sheet
@Test
public void repeatedWrite() {
String fileName = "D:\\demo1.xlsx";
// 這裡 指定文件
try (ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelEntity.class).build()) {
// 去調用寫入,這裡我調用了五次,實際使用時根據資料庫分頁的總的頁數來。這裡最終會寫到5個sheet裡面
for (int i = 0; i < 5; i++) {
// 每次都要創建writeSheet 這裡註意必須指定sheetNo 而且sheetName必須不一樣
WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();
// 分頁去資料庫查詢數據 這裡可以去資料庫查詢每一頁的數據
List<ExcelEntity> data = data();
excelWriter.write(data, writeSheet);
}
}
}
7.不同的對象寫到不同的sheet
@Test
public void repeatedWrite() {
String fileName = "D:\\demo1.xlsx";
// 這裡 指定文件
try (ExcelWriter excelWriter = EasyExcel.write(fileName).build()) {
// 去調用寫入,這裡我調用了五次,實際使用時根據資料庫分頁的總的頁數來。這裡最終會寫到5個sheet裡面
for (int i = 0; i < 5; i++) {
// 每次都要創建writeSheet 這裡註意必須指定sheetNo 而且sheetName必須不一樣。這裡註意DemoData.class 可以每次都變,我這裡為了方便 所以用的同一個class
// 實際上可以一直變
WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).head(ExcelEntity.class).build();
// 分頁去資料庫查詢數據 這裡可以去資料庫查詢每一頁的數據
List<ExcelEntity> data = data();
excelWriter.write(data, writeSheet);
}
}
}
讀 Excel
1.以實體類映射想要讀取的 Excel 數據
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExcelEntity {
private String string;
private Date date;
private Double doubleData;
}
2.持久層
/**
* 假設這個是你的DAO存儲。當然還要這個類讓spring管理,當然你不用需要存儲,也不需要這個類。
**/
public class ExcelDAO {
public void save(List<ExcelEntity> list) {
// 如果是mybatis,儘量別直接調用多次insert,自己寫一個mapper裡面新增一個方法batchInsert,所有數據一次性插入
}
}
3.監聽器
// 有個很重要的點 ExcelEntityListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
public class ExcelEntityListener implements ReadListener<ExcelEntity> {
/**
* 每隔5條存儲資料庫,實際使用中可以100條,然後清理list ,方便記憶體回收
*/
private static final int BATCH_COUNT = 100;
/**
* 緩存的數據
*/
private List<ExcelEntity> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假設這個是一個DAO,當然有業務邏輯這個也可以是一個service。當然如果不用存儲這個對象沒用。
*/
private ExcelDAO excelDAO;
public ExcelEntityListener() {
// 這裡是demo,所以隨便new一個。實際使用如果到了spring,請使用下麵的有參構造函數
excelDAO = new ExcelDAO();
}
/**
* 如果使用了spring,請使用這個構造方法。每次創建Listener的時候需要把spring管理的類傳進來
*
* @param excelDAO
*/
public ExcelEntityListener(ExcelDAO excelDAO) {
this.excelDAO = excelDAO;
}
/**
* 這個每一條數據解析都會來調用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(ExcelEntity data, AnalysisContext context) {
cachedDataList.add(data);
// 達到BATCH_COUNT了,需要去存儲一次資料庫,防止數據幾萬條數據在記憶體,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存儲完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有數據解析完成了 都會來調用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 這裡也要保存數據,確保最後遺留的數據也存儲到資料庫
saveData();
}
/**
* 加上存儲資料庫
*/
private void saveData() {
excelDAO.save(cachedDataList);
}
}
4.讀取 Excel 數據
@Test
public void simpleRead() {
// 寫法1:JDK8+ ,不用額外寫一個DemoDataListener
// since: 3.0.0-beta1
String fileName = "D:\\damo1.xlsx";
// 這裡預設每次會讀取100條數據 然後返回過來 直接調用使用數據就行
// 具體需要返回多少行可以在`PageReadListener`的構造函數設置
EasyExcel.read(fileName, ExcelEntity.class, new PageReadListener<ExcelEntity>(dataList -> {
for (ExcelEntity demoData : dataList) {
log.info("讀取到一條數據{}", JSON.toJSONString(demoData));
}
})).sheet().doRead();
// 寫法2:
// 匿名內部類 不用額外寫一個DemoDataListener
fileName = "D:\\damo1.xlsx";
// 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 文件流會自動關閉
EasyExcel.read(fileName, ExcelEntity.class, new ReadListener<ExcelEntity>() {
/**
* 單次緩存的數據量
*/
public static final int BATCH_COUNT = 100;
/**
*臨時存儲
*/
private List<ExcelEntity> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override
public void invoke(ExcelEntity data, AnalysisContext context) {
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存儲完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
}
/**
* 加上存儲資料庫
*/
private void saveData() {
log.info("{}條數據,開始存儲資料庫!", cachedDataList.size());
log.info("存儲資料庫成功!");
}
}).sheet().doRead();
// 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
// 寫法3:
fileName = "D:\\damo1.xlsx";
// 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 文件流會自動關閉
EasyExcel.read(fileName, ExcelEntity.class, new ExcelEntityListener()).sheet().doRead();
// 寫法4
fileName = "D:\\damo1.xlsx";
// 一個文件一個reader
try (ExcelReader excelReader = EasyExcel.read(fileName, ExcelEntity.class, new ExcelEntityListener()).build()) {
// 構建一個sheet 這裡可以指定名字或者no
ReadSheet readSheet = EasyExcel.readSheet(0).build();
// 讀取一個sheet
excelReader.read(readSheet);
}
}
}