說明
最近有很多數據要處理,需要導出Excel表格。在使用POI時發現非常耗內存,在GitHub上發現阿里開源的Easy Excel項目,基於java的讀寫Excel,十分省內存。本篇博文主要是總結記錄了使用EasyExcel進行寫Excel的方法。
正文
引入依賴
在pom.xml文件引入easyexcel依賴,我使用的是2.1.4版本,目前最新是2.1.6。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.4</version>
</dependency>
簡單寫Excel
這是最基本的使用方式。通過在pojo類的屬性上使用@ExcelProperty(“列名”)註解來指定該屬性所屬列名,通過@ExcelIgnore註解來忽略該屬性。不同與之前的1.x版本,數據的pojo類不需要再繼承BaseModel類。
在官方指導文件中,提供了兩種簡單寫的方法:
- 直接通過EasyExcel調用write方法
EasyExcel.write(fileName, clazz).sheet(sheetName).doWrite(dataList);
- 分別創建ExcelWriter和WriteSheet對象,調用excelWriter的write方法,並且在寫完後要手動調用excelWriter的finish方法關閉流。
ExcelWriter excelWriter = EasyExcel.write(fileName, claxx).build();
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
excelWriter.write(dataList, writeSheet);
excelWriter.finish();
在使用中,封裝了一個Util類,通過createExcel進行簡單的寫:
public static <T> void createExcel(String filePath, List<T> dataList, String sheetName, Class<T> clazz) throws Exception {
try {
EasyExcel.write(filePath, clazz).sheet(sheetName).doWrite(dataList);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
動態生成複雜頭寫Excel
在導出數據要求中,需要針對每天的數據進行統計寫入表格,而日期範圍是不確定的,這就需要動態生成表頭,並且在要求中,數據統計還分多個維度。所以,寫excel的同時要先生成相對應的複雜頭。
在官方文檔中,有示例介紹如何實現負責頭的寫入。在pojo類中,對每個屬性使用了@ExcelProperty({“主標題”, “子標題”})註解指定了屬性對應列的複雜頭名稱。
示例:
public class ComplexHeadData {
@ExcelProperty({"主標題", "字符串標題"})
private String string;
@ExcelProperty({"主標題", "日期標題"})
private Date date;
@ExcelProperty({"主標題", "數字標題"})
private Double doubleData;
....省略set get方法
}
同樣通過在EasyExcel.write方法進行寫入。
我們知道了固定複雜頭的寫入方式,那麼動態複雜頭的寫入,第一步就是要創建複雜頭的List<List>。
如果要想得到與上示代碼同樣的複雜頭結構,則需要:
public List<List<String>> getHead() {
List<List<String>> headList = new ArrayList<>();
List<String> head1 = new ArrayList<>();
head1.add("主標題");
head1.add("字符串標題");
headList.add(head1);
List<String> head2 = new ArrayList<>();
head2.add("主標題");
head2.add("日期標題");
headList.add(head2);
List<String> head3 = new ArrayList<>();
head3.add("主標題");
head3.add("數字標題");
headList.add(head3);
List<String> head4 = new ArrayList<>();
head4.add("主標題II");
headList.add(head4);
return headList;
}
在構造數據時,也應該是List<List>,每個List表示表格中的一行數據。注意,list中數據中的順序要和生成頭中列名的順序保持一致。
public List<List<String>> getdata() {
List<List<String>> dataList = new ArrayList<>();
List<String> list1 = new ArrayList<>();
list1.add("1");
list1.add("2");
list1.add("3");
list1.add("4");
dataList.add(list1);
List<String> list2 = new ArrayList<>();
list2.add("5");
list2.add("6");
list2.add("7");
list2.add("8");
dataList.add(list2);
return dataList;
}
導出表格示例:
同樣,在Util類中封裝了creatExcelDynamicHead方法。
public static <T> void createExcelDynamicHead(String filePath, List<T> dataList, List<List<String>> head, String sheetName) throws Exception {
try {
EasyExcel.write(filePath).head(head).sheet(sheetName).doWrite(dataList);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
重複多個sheet寫Excel
在數據要求中,需要一個Excel中包含多個sheet,這就需要一個excel多次寫入。在官方文檔中,對此重複多次寫入(寫到單個或者多個Sheet)有詳細的介紹。總共介紹了三種方法,每種實現不同的功能:
- 數據分批寫到同一個sheet
// 這裏 需要指定寫用哪個class去寫
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// 這裏注意 如果同一個sheet只要創建一次
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
// 去調用寫入,這裏我調用了五次,實際使用時根據數據庫分頁的總的頁數來
for (int i = 0; i < 5; i++) {
// 分頁去數據庫查詢數據 這裏可以去數據庫查詢每一頁的數據
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
// 千萬別忘記finish 會幫忙關閉流
excelWriter.finish();
- 寫到多個不同的sheet
// 方法2 如果寫到不同的sheet 同一個對象
fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
// 這裏 指定文件
excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// 去調用寫入,這裏我調用了五次,實際使用時根據數據庫分頁的總的頁數來。這裏最終會寫到5個sheet裏面
for (int i = 0; i < 5; i++) {
// 每次都要創建writeSheet 這裏注意必須指定sheetNo
writeSheet = EasyExcel.writerSheet(i, "模板").build();
// 分頁去數據庫查詢數據 這裏可以去數據庫查詢每一頁的數據
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
// 千萬別忘記finish 會幫忙關閉流
excelWriter.finish();
- 寫多個sheet,數據不同
與第二種方法類似,但不同的是,在每次新建WriteSheet對象時,需要指定不同的數據類對象。
for (int i = 0; i < 5; i++) {
// 每次都要創建writeSheet 這裏注意必須指定sheetNo。這裏注意DemoData.class 可以每次都變,我這裏爲了方便 所以用的同一個class 實際上可以一直變
writeSheet = EasyExcel.writerSheet(i, "模板").head(DemoData.class).build();
.....
}
在Util類中,封裝了createExcelManySheets方法,該方法是對第二種使用相同的數據寫入多個sheet方法的封裝。datMap的key爲不同sheet的名稱。
public static <T> void createExcelManySheets(String filePath, Map<String, List<T>> dataMap, Class<T> clazz) throws Exception {
ExcelWriter excelWriter = EasyExcel.write(filePath, clazz).build();
try {
int index = 0;
for (Map.Entry<String, List<T>> entry : dataMap.entrySet()) {
String sheetName = entry.getKey();
List<T> data = entry.getValue();
WriteSheet sheet = EasyExcel.writerSheet(index++, sheetName).build();
excelWriter.write(data, sheet);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
web中的寫入下載
通過該方法對生成的excel進行下載。在官方文檔中也有詳細的示例:
將數據寫入到HttpServletResponse的輸出流outputstream中。
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
// 這裏注意 有同學反應使用swagger 會導致各種問題,請直接用瀏覽器或者用postman
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 這裏URLEncoder.encode可以防止中文亂碼 當然和easyexcel沒有關係
String fileName = URLEncoder.encode("測試", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
可以發現,與以上調用EasyExcel.write方法進行寫入不同的是將filename替換爲了response的輸出流。
EasyExcelUtil
public class EasyExcelUtil {
public static <T> void createExcel(String filePath, List<T> dataList, String sheetName, Class<T> clazz) throws Exception {
try {
EasyExcel.write(filePath, clazz).sheet(sheetName).doWrite(dataList);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> void createExcelDynamicHead(String filePath, List<T> dataList, List<List<String>> head, String sheetName) throws Exception {
try {
EasyExcel.write(filePath).head(head).sheet(sheetName).doWrite(dataList);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> void createExcelManySheets(String filePath, Map<String, List<T>> dataMap, Class<T> clazz) throws Exception {
ExcelWriter excelWriter = EasyExcel.write(filePath, clazz).build();
try {
int index = 0;
for (Map.Entry<String, List<T>> entry : dataMap.entrySet()) {
String sheetName = entry.getKey();
List<T> data = entry.getValue();
WriteSheet sheet = EasyExcel.writerSheet(index++, sheetName).build();
excelWriter.write(data, sheet);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
public static <T> void createExcelWeb(OutputStream outputStream, List<T> dataList, String sheetName, Class<T> clazz) throws Exception{
try {
EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(dataList);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> void createExcelDynamicHeadWeb(OutputStream outputStream, List<T> dataList, List<List<String>> head, String sheetName) throws Exception {
try {
EasyExcel.write(outputStream).head(head).sheet(sheetName).doWrite(dataList);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> void createExcelManySheetsWeb(OutputStream outputStream, Map<String, List<T>> dataMap, Class<T> clazz) throws Exception {
ExcelWriter excelWriter = EasyExcel.write(outputStream, clazz).build();
try {
int index = 0;
for (Map.Entry<String, List<T>> entry : dataMap.entrySet()) {
String sheetName = entry.getKey();
List<T> data = entry.getValue();
WriteSheet sheet = EasyExcel.writerSheet(index++, sheetName).build();
excelWriter.write(data, sheet);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
}
更多的使用方式請查閱官方文檔。
參考資料:
https://alibaba-easyexcel.github.io/index.html
https://www.cnblogs.com/oukele/p/11444234.html