背景
團隊目前在做一個用戶數據看板(下面簡稱看板),基本覆蓋用戶的所有行為數據,並生成分析報表,用戶行為由多個數據來源組成(餐飲、生活日用、充值消費、交通出行、通訊物流、交通出行、醫療保健、住房物業、運動健康...),
基於大量數據的組合、排序和統計。根據最新的統計報告,每天將近100W+的行為數據產生,所以這個數據基數是非常大的。
而這個數據中心,對接很多的業務團隊,這些團隊根據自己的需要,對某些維度進行篩選,然后直接從我們的中心上下載數據(excel)文檔進行分析。所以下個幾十萬上百萬行的數據是很常見的。
問題和解決方案
遇到的問題
目前遇到的主要問題是,隨着行為能力逐漸的完善閉環,用戶數據沉淀的也越來越多了,同時業務量的也在不斷擴大。
業務團隊有時候會下載超量的數據來進行分析,平台上的數據下載能力就顯得尤為重要了。而我們的問題是下載效率太慢,10W的數據大約要5分鍾以上才能下載下來,這顯然有問題了。
解決步驟
代碼是之前團隊遺留的,原先功能沒開放使用,沒有數據量,所以沒有發現問題。以下是原來的導出模塊,原程序如下,我做了基本還原。
現在如何保證數據的高效導出是我們最重要的目標,這個也是業務團隊最關心的。
1 /** 2 * 獲取導出的Excel的文件流信息 3 * @param exportData 4 * @return 5 * @throws Exception 6 */ 7 private OutputStream getExportOutPutStream(List<UBehavDto> exportData) throws Exception { 8 JSONObject object = new JSONObject(); 9 List<ExcelCell[]> excelCells = new ArrayList<>(); 10 String[] headers = new String[] { "A字段","B字段","C字段","D","E","F","G","H","I","J","K","L", 11 "M","N","O","P","Q","R","S","T","U","V","W", 12 "X","Y","Z","AA","AB","AC","AD","AE字段","AF字段","AG字段" }; 13 ExcelCell[] headerRow = getHeaderRow(headers); 14 excelCells.add(headerRow); 15 String pattern = "yyyy-MM-dd hh:mm:ss"; 16 for (UBehavDto uBehavDto:exportData) { 17 String[] singleRow = new String[] { uBehavDto.getA(),uBehavDto.getB(),uBehavDto.getC(),uBehavDto.getD(),uBehavDto.getE(),uBehavDto.getF(), 18 DateFormatUtils.format(uBehavDto.getAddTime(), pattern),DateFormatUtils.format(uBehavDto.getDate(), pattern), 19 uBehavDto.getG(),uBehavDto.getH(),uBehavDto.getI(),uBehavDto.getJ(),uBehavDto.getK(),uBehavDto.getL(),uBehavDto.getM(), 20 uBehavDto.getN(),uBehavDto.getO(),uBehavDto.getP(), 21 uBehavDto.getQ(),uBehavDto.getR(),uBehavDto.getS(),String.valueOf(uBehavDto.getT()),uBehavDto.getMemo(),uBehavDto.getU(),uBehavDto.getV(), 22 uBehavDto.getW(),uBehavDto.getX(), 23 uBehavDto.getY(),uBehavDto.getZ(),uBehavDto.getAA(),uBehavDto.getAB(),uBehavDto.getAC() }; 24 ExcelCell[] cells = new ExcelCell[singleRow.length]; 25 ExcelCell getA=new ExcelCell();getA.setValue(uBehavDto.getA()); 26 ExcelCell getB=new ExcelCell();getB.setValue(uBehavDto.getB()); 27 ExcelCell getC=new ExcelCell();getC.setValue(uBehavDto.getC()); 28 ExcelCell getD=new ExcelCell();getD.setValue(uBehavDto.getD()); 29 ExcelCell getE=new ExcelCell();getE.setValue(uBehavDto.getE()); 30 ExcelCell getF=new ExcelCell();getF.setValue(uBehavDto.getF()); 31 ExcelCell getAddTime=new ExcelCell();getAddTime.setValue(DateFormatUtils.format(uBehavDto.getAddTime(), pattern)); 32 ExcelCell getDate=new ExcelCell();getDate.setValue(DateFormatUtils.format(uBehavDto.getDate(), pattern)); 33 ExcelCell getG=new ExcelCell();getG.setValue(uBehavDto.getG()); 34 ExcelCell getH=new ExcelCell();getH.setValue(uBehavDto.getH()); 35 ExcelCell getI=new ExcelCell();getI.setValue(uBehavDto.getI()); 36 ExcelCell getJ=new ExcelCell();getJ.setValue(uBehavDto.getJ()); 37 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getK()); 38 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getL()); 39 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getM()); 40 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getN()); 41 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getO()); 42 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getP()); 43 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getQ()); 44 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getR()); 45 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getS()); 46 ExcelCell a=new ExcelCell();a.setValue(String.valueOf(uBehavDto.getT())); 47 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getMemo()); 48 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getU()); 49 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getV()); 50 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getW()); 51 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getX()); 52 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getY()); 53 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getZ()); 54 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getAA()); 55 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getAB()); 56 ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getAC()); 57 ExcelCell[] cells = { 58 new ExcelCell(uBehavDto.getA()), 59 new ExcelCell().setValue(uBehavDto.getB()), 60 new ExcelCell().setValue(uBehavDto.getC()), 61 new ExcelCell().setValue(uBehavDto.getD()), 62 new ExcelCell().setValue(uBehavDto.getE()), 63 new ExcelCell().setValue(uBehavDto.getF()), 64 new ExcelCell().setValue(DateFormatUtils.format(uBehavDto.getAddTime(), pattern)), 65 new ExcelCell().setValue(DateFormatUtils.format(uBehavDto.getDate(), pattern)), 66 new ExcelCell().setValue(uBehavDto.getG()), 67 new ExcelCell().setValue(uBehavDto.getH()), 68 new ExcelCell().setValue(uBehavDto.getI()), 69 new ExcelCell().setValue(uBehavDto.getJ()), 70 new ExcelCell().setValue(uBehavDto.getK()), 71 new ExcelCell().setValue(uBehavDto.getL()), 72 new ExcelCell().setValue(uBehavDto.getM()), 73 new ExcelCell().setValue(uBehavDto.getN()), 74 new ExcelCell().setValue(uBehavDto.getO()), 75 new ExcelCell().setValue(uBehavDto.getP()), 76 new ExcelCell().setValue(uBehavDto.getQ()), 77 new ExcelCell().setValue(uBehavDto.getR()), 78 new ExcelCell().setValue(uBehavDto.getS()), 79 new ExcelCell().setValue(String.valueOf(uBehavDto.getT())), 80 new ExcelCell().setValue(uBehavDto.getMemo()), 81 new ExcelCell().setValue(uBehavDto.getU()), 82 new ExcelCell().setValue(uBehavDto.getV()), 83 new ExcelCell().setValue(uBehavDto.getW()), 84 new ExcelCell().setValue(uBehavDto.getX()), 85 new ExcelCell().setValue(uBehavDto.getY()), 86 new ExcelCell().setValue(uBehavDto.getZ()), 87 new ExcelCell().setValue(uBehavDto.getAA()), 88 new ExcelCell().setValue(uBehavDto.getAB()), 89 new ExcelCell().setValue(uBehavDto.getAC()) 90 }; 91 92 for(int idx=0;idx<singleRow.length;idx++) { 93 ExcelCell cell = new ExcelCell(); 94 cell.setValue(singleRow[idx]); 95 cells[idx] = cell; 96 } 97 excelCells.add(cells); 98 } 99 object.put("行為數據", excelCells); 100 ExcelUtils utils = new ExcelUtils(); 101 OutputStream outputStream = utils.writeExcel(object); 102 return outputStream; 103 }
看看標紅的代碼,這個生成Excel的方式是對Excel中的每一個cell進行渲染,逐行的進行數據填充,效率太慢了,根據日志分析發現:基本時間都耗費在數據生成Excel上。每生成1W左右的數據基本
消耗1分鍾的時間。原來在其他業務中他只是作為簡量數據導出來使用,比如幾百條的數據,很快就出來了,但是遇到大量數據導出的情況,性能問題就立馬現形了。
團隊內討論了一下並參考了資料,發現原來業內有很多好用強大的Excel處理組件,我們優先選用阿里的easy excel來做一下嘗試。
Pom添加 easyexcel 如下:
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.4</version> </dependency>
代碼:dto內容(中文為配置好的表頭):
1 package com.xxx.xxx.modules.worklog.dto; 2 3 import com.alibaba.excel.annotation.ExcelProperty; 4 import lombok.Getter; 5 import lombok.Setter; 6 import java.io.Serializable; 7 import java.util.Date; 8 9 /** 10 * <p>Description:XX表基本信息 </p> 11 * <p>Copyright: Copyright (c) 2021 </p> 12 * <p>Company: XX Co., Ltd. </p> 13 * 14 * @author brand 15 * @date 2021-06-26 10:07:46 16 * <p>Update Time: </p> 17 * <p>Updater: </p> 18 * <p>Update Comments: </p> 19 */ 20 @Setter 21 @Getter 22 public class WorkLogDto implements Serializable { 23 private static final long serialVersionUID = -5523294561640180605L; 24 @ExcelProperty("A字段") 25 private String aClolumn; 26 @ExcelProperty("B字段") 27 private String BColumn; 28 @ExcelProperty("C字段") 29 private String cColumn; 30 @ExcelProperty("D字段") 31 private String dColumn; 32 @ExcelProperty("E字段") 33 private String eColumn; 34 @ExcelProperty("F字段") 35 private String fColumn; 36 @ExcelProperty("G字段") 37 private Date gColumn; 38 @ExcelProperty("H字段") 39 private Date hColumn; 40 @ExcelProperty("I字段") 41 private String iColumn; 42 @ExcelProperty("J字段") 43 private String jColumn; 44 @ExcelProperty("K字段") 45 private String kColumn; 46 @ExcelProperty("L字段") 47 private String lColumn; 48 @ExcelProperty("M字段") 49 private String mColumn; 50 @ExcelProperty("N字段") 51 private String nColumn; 52 @ExcelProperty("O字段") 53 private String oColumn; 54 @ExcelProperty("P字段") 55 private String pColumn; 56 @ExcelProperty("Q字段") 57 private String qColumn; 58 @ExcelProperty("R字段") 59 private String rColumn; 60 @ExcelProperty("S字段") 61 private String sColumn; 62 @ExcelProperty("T字段") 63 private String tColumn; 64 @ExcelProperty("U字段") 65 private String uColumn; 66 @ExcelProperty("V字段") 67 private double vColumn; 68 @ExcelProperty("W字段") 69 private String wColumn; 70 @ExcelProperty("X字段") 71 private String xClumn; 72 @ExcelProperty("Y字段") 73 private String yColumn; 74 @ExcelProperty("Z字段") 75 private String zColumn; 76 }
生成文件流的步驟(代碼很清晰了):
1 /** 2 * EasyExcel 生成文件流 3 * @param exportData 4 * @return 5 */ 6 private byte[] getEasyExcelOutPutStream(List<WorkLogDto> exportData) { 7 try { 8 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); 9 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); 10 contentWriteCellStyle.setWrapped(true); 11 HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); 12 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 13 EasyExcel.write(outputStream, WorkLogDto.class).sheet("行為業務數據") // Sheet名稱 14 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) 15 .registerWriteHandler(horizontalCellStyleStrategy) 16 .doWrite(exportData); 17 byte[] bytes = outputStream.toByteArray(); 18 // 關閉流 19 outputStream.close(); 20 return bytes; 21 } 22 catch (Exception ex) { 23 log.error("輸出Excel文件流失敗:"+ex.getMessage()); 24 return null; 25 } 26 }
完整生成Excel文件流並上傳:
1 /** 2 * 上傳用戶數據報表 3 * @param prmWorkLogExport 4 * @param order 5 * @param orderType 6 * @return 7 */ 8 @Override 9 @Async 10 public Object uploadWorkLogData(PrmWorkLogExport prmWorkLogExport,ExportTaskDomain domain, String order, String orderType,String suid) { 11 try { 12 log.info(String.format("ExportWorkLog->:%s", "開始獲取數據")); 13 List<WorkLogDto> logList = getLogList(prmWorkLogExport,order,orderType); 14 log.info(String.format("ExportWorkLog->:結束獲取數據,總 %d 條數據", logList.size())); 15 byte[] bytes = getEasyExcelOutPutStream(logList); 16 log.info(String.format("ExportWorkLog->:%s","完成數據轉excel文件流")); 17 /* 暫時作廢 Todo 18 int max=55;int min=40; 19 Random random = new Random(); 20 int rd = random.nextInt(max)%(max-min+1) + min; 21 modifyExportTask(domain.getId(),0,rd);//計算生成數據的進度 22 */ 23 //開始投遞文件集群服務器,並將結果反寫到數據庫 24 log.info(String.format("ExportWorkLog->:%s","開始將數據寫入文件服務系統")); 25 Dentry dentry = csService.coverUploadByByteArrayByToken(domain, bytes); 26 //執行異步記錄,以免連接池關閉 27 executor.execute(() -> { 28 try { 29 asynworkService.finishExportTask(domain.getId(),domain.getFileName(), dentry); 30 } catch (Exception e) { 31 log.error("更新任務狀態失敗:", e.getMessage()); 32 } 33 }); 34 35 } catch (Exception ex) { 36 // 1完成 0進行中 2生產錯誤 37 String updateSql = String.format(" update exporttask set statu=2 where taskid=%s;",domain.getId()); 38 Query query = entityManager.createNativeQuery(updateSql); 39 query.executeUpdate(); 40 entityManager.flush(); 41 entityManager.clear(); 42 log.info(String.format("ExportWorkLog->:上傳文件異常:%s",ex.getMessage())); 43 } 44 return null; 45 }
改用阿里 easyexcel 組件后,10W+ 的數據從生成Excel文件流到上傳只要8秒,原來約要8分鍾 ,以下為各個步驟時間點的日志記錄,可以看出時間消耗:

整理工具類
工具類和使用說明
參考網上整理的工具類,有些類、方法在之前的版本是ok的,新版本下被標記為過時了。
1 package com.nd.helenlyn.common.utils; 2 3 import com.alibaba.excel.EasyExcelFactory; 4 import com.alibaba.excel.ExcelWriter; 5 import com.alibaba.excel.context.AnalysisContext; 6 import com.alibaba.excel.event.AnalysisEventListener; 7 import com.alibaba.excel.metadata.BaseRowModel; 8 import com.alibaba.excel.metadata.Sheet; 9 import lombok.Data; 10 import lombok.Getter; 11 import lombok.Setter; 12 import lombok.extern.slf4j.Slf4j; 13 import org.springframework.util.CollectionUtils; 14 import org.springframework.util.StringUtils; 15 16 import java.io.FileInputStream; 17 import java.io.FileNotFoundException; 18 import java.io.FileOutputStream; 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.io.OutputStream; 22 import java.util.ArrayList; 23 import java.util.Collections; 24 import java.util.List; 25 26 /** 27 * @author brand 28 * @Description: 29 * @Copyright: Copyright (c) 2021 30 * @Company: XX, Inc. All Rights Reserved. 31 * @date 2021/7/10 3:54 下午 32 * @Update Time: 33 * @Updater: 34 * @Update Comments: 35 */ 36 @Slf4j 37 public class EasyExcelUtil { 38 private static Sheet initSheet; 39 static { 40 initSheet = new Sheet(1, 0); 41 initSheet.setSheetName("sheet"); 42 //設置自適應寬度,避免表頭重疊情況 43 initSheet.setAutoWidth(Boolean.TRUE); 44 } 45 46 /** 47 * 讀取少於1000行數據的情況 48 * @param filePath 文件存放的絕對路徑 49 * @return 50 */ 51 public static List<Object> lessThan1000Row(String filePath){ 52 return lessThan1000RowBySheet(filePath,null); 53 } 54 55 /** 56 * 讀小於1000行數據, 帶樣式 57 * filePath 文件存放的絕對路徑 58 * initSheet : 59 * sheetNo: sheet頁碼,默認為1 60 * headLineMun: 從第幾行開始讀取數據,默認為0, 表示從第一行開始讀取 61 * clazz: 返回數據List<Object> 中Object的類名 62 */ 63 public static List<Object> lessThan1000RowBySheet(String filePath, Sheet sheet){ 64 if(!StringUtils.hasText(filePath)){ 65 return null; 66 } 67 68 sheet = sheet != null ? sheet : initSheet; 69 70 InputStream fileStream = null; 71 try { 72 fileStream = new FileInputStream(filePath); 73 return EasyExcelFactory.read(fileStream, sheet); 74 } catch (FileNotFoundException e) { 75 log.info("找不到文件或文件路徑錯誤, 文件:{}", filePath); 76 }finally { 77 try { 78 if(fileStream != null){ 79 fileStream.close(); 80 } 81 } catch (IOException e) { 82 log.info("excel文件讀取失敗, 失敗原因:{}", e); 83 } 84 } 85 return null; 86 } 87 88 /** 89 * 讀大於1000行數據 90 * @param filePath 文件存放的絕對路徑 91 * @return 92 */ 93 public static List<Object> mareThan1000Row(String filePath){ 94 return moreThan1000RowBySheet(filePath,null); 95 } 96 97 /** 98 * 讀大於1000行數據, 帶樣式 99 * @param filePath 文件存放的絕對路徑 100 * @return 101 */ 102 public static List<Object> moreThan1000RowBySheet(String filePath, Sheet sheet){ 103 if(!StringUtils.hasText(filePath)){ 104 return null; 105 } 106 107 sheet = sheet != null ? sheet : initSheet; 108 109 InputStream fileStream = null; 110 try { 111 fileStream = new FileInputStream(filePath); 112 ExcelListener excelListener = new ExcelListener(); 113 EasyExcelFactory.readBySax(fileStream, sheet, excelListener); 114 return excelListener.getDatas(); 115 } catch (FileNotFoundException e) { 116 log.error("找不到文件或文件路徑錯誤, 文件:{}", filePath); 117 }finally { 118 try { 119 if(fileStream != null){ 120 fileStream.close(); 121 } 122 } catch (IOException e) { 123 log.error("excel文件讀取失敗, 失敗原因:{}", e); 124 } 125 } 126 return null; 127 } 128 129 /** 130 * 生成excle 131 * @param filePath 絕對路徑, 如:/home/{user}/Downloads/123.xlsx 132 * @param data 數據源 133 * @param head 表頭 134 */ 135 public static void writeBySimple(String filePath, List<List<Object>> data, List<String> head){ 136 writeSimpleBySheet(filePath,data,head,null); 137 } 138 139 /** 140 * 生成excle 141 * @param filePath 絕對路徑, 如:/home/{user}/Downloads/123.xlsx 142 * @param data 數據源 143 * @param sheet excle頁面樣式 144 * @param head 表頭 145 */ 146 public static void writeSimpleBySheet(String filePath, List<List<Object>> data, List<String> head, Sheet sheet){ 147 sheet = (sheet != null) ? sheet : initSheet; 148 149 if(head != null){ 150 List<List<String>> list = new ArrayList<>(); 151 head.forEach(h -> list.add(Collections.singletonList(h))); 152 sheet.setHead(list); 153 } 154 155 OutputStream outputStream = null; 156 ExcelWriter writer = null; 157 try { 158 outputStream = new FileOutputStream(filePath); 159 writer = EasyExcelFactory.getWriter(outputStream); 160 writer.write1(data,sheet); 161 } catch (FileNotFoundException e) { 162 log.error("找不到文件或文件路徑錯誤, 文件:{}", filePath); 163 }finally { 164 try { 165 if(writer != null){ 166 writer.finish(); 167 } 168 169 if(outputStream != null){ 170 outputStream.close(); 171 } 172 173 } catch (IOException e) { 174 log.error("excel文件導出失敗, 失敗原因:{}", e); 175 } 176 } 177 178 } 179 180 /** 181 * 生成excle 182 * @param filePath 文件存放的絕對路徑, 如:/home/{user}/Downloads/123.xlsx 183 * @param data 數據源 184 */ 185 public static void writeWithTemplate(String filePath, List<? extends BaseRowModel> data){ 186 writeWithTemplateAndSheet(filePath,data,null); 187 } 188 189 /** 190 * 生成excle 191 * @param filePath 文件存放的絕對路徑, 如:/home/user/Downloads/123.xlsx 192 * @param data 數據源 193 * @param sheet excle頁面樣式 194 */ 195 public static void writeWithTemplateAndSheet(String filePath, List<? extends BaseRowModel> data, Sheet sheet){ 196 if(CollectionUtils.isEmpty(data)){ 197 return; 198 } 199 200 sheet = (sheet != null) ? sheet : initSheet; 201 sheet.setClazz(data.get(0).getClass()); 202 203 OutputStream outputStream = null; 204 ExcelWriter writer = null; 205 try { 206 outputStream = new FileOutputStream(filePath); 207 writer = EasyExcelFactory.getWriter(outputStream); 208 writer.write(data,sheet); 209 } catch (FileNotFoundException e) { 210 log.error("找不到文件或文件路徑錯誤, 文件:{}", filePath); 211 }finally { 212 try { 213 if(writer != null){ 214 writer.finish(); 215 } 216 217 if(outputStream != null){ 218 outputStream.close(); 219 } 220 } catch (IOException e) { 221 log.error("excel文件導出失敗, 失敗原因:{}", e); 222 } 223 } 224 225 } 226 227 /** 228 * 生成多Sheet的excle 229 * @param filePath 絕對路徑, 如:/home/{user}/Downloads/123.xlsx 230 * @param multipleSheelPropetys 231 */ 232 public static void writeWithMultipleSheel(String filePath,List<MultipleSheelPropety> multipleSheelPropetys){ 233 if(CollectionUtils.isEmpty(multipleSheelPropetys)){ 234 return; 235 } 236 237 OutputStream outputStream = null; 238 ExcelWriter writer = null; 239 try { 240 outputStream = new FileOutputStream(filePath); 241 writer = EasyExcelFactory.getWriter(outputStream); 242 for (MultipleSheelPropety multipleSheelPropety : multipleSheelPropetys) { 243 Sheet sheet = multipleSheelPropety.getSheet() != null ? multipleSheelPropety.getSheet() : initSheet; 244 if(!CollectionUtils.isEmpty(multipleSheelPropety.getData())){ 245 sheet.setClazz(multipleSheelPropety.getData().get(0).getClass()); 246 } 247 writer.write(multipleSheelPropety.getData(), sheet); 248 } 249 250 } catch (FileNotFoundException e) { 251 log.error("找不到文件或文件路徑錯誤, 文件:{}", filePath); 252 }finally { 253 try { 254 if(writer != null){ 255 writer.finish(); 256 } 257 258 if(outputStream != null){ 259 outputStream.close(); 260 } 261 } catch (IOException e) { 262 log.error("excel文件導出失敗, 失敗原因:{}", e); 263 } 264 } 265 266 } 267 268 269 /*********************以下為內部類,可以提取到獨立類中******************************/ 270 271 @Data 272 public static class MultipleSheelPropety{ 273 274 private List<? extends BaseRowModel> data; 275 276 private Sheet sheet; 277 } 278 279 /** 280 * 解析監聽器, 281 * 每解析一行會回調invoke()方法。 282 * 整個excel解析結束會執行doAfterAllAnalysed()方法 283 * 284 * @author: chenmingjian 285 * @date: 19-4-3 14:11 286 */ 287 @Getter 288 @Setter 289 public static class ExcelListener extends AnalysisEventListener { 290 291 private List<Object> datas = new ArrayList<>(); 292 293 /** 294 * 逐行解析 295 * object : 當前行的數據 296 */ 297 @Override 298 public void invoke(Object object, AnalysisContext context) { 299 //當前行 300 // context.getCurrentRowNum() 301 if (object != null) { 302 datas.add(object); 303 } 304 } 305 306 307 /** 308 * 解析完所有數據后會調用該方法 309 */ 310 @Override 311 public void doAfterAllAnalysed(AnalysisContext context) { 312 //解析結束銷毀不用的資源 313 } 314 315 } 316 }
參考資料
語雀例子文檔:https://www.yuque.com/easyexcel/doc/easyexcel
easyexcel GitHub地址:https://github.com/alibaba/easyexcel