Java大批量導(dǎo)出Excel數(shù)據(jù)的優(yōu)化過(guò)程
背景
團(tuán)隊(duì)目前在做一個(gè)用戶數(shù)據(jù)看板(下面簡(jiǎn)稱看板),基本覆蓋用戶的所有行為數(shù)據(jù),并生成分析報(bào)表,用戶行為由多個(gè)數(shù)據(jù)來(lái)源組成(餐飲、生活日用、充值消費(fèi)、交通出行、通訊物流、交通出行、醫(yī)療保健、住房物業(yè)、運(yùn)動(dòng)健康...),
基于大量數(shù)據(jù)的組合、排序和統(tǒng)計(jì)。根據(jù)最新的統(tǒng)計(jì)報(bào)告,每天將近100W+的行為數(shù)據(jù)產(chǎn)生,所以這個(gè)數(shù)據(jù)基數(shù)是非常大的。
而這個(gè)數(shù)據(jù)中心,對(duì)接很多的業(yè)務(wù)團(tuán)隊(duì),這些團(tuán)隊(duì)根據(jù)自己的需要,對(duì)某些維度進(jìn)行篩選,然后直接從我們的中心上下載數(shù)據(jù)(excel)文檔進(jìn)行分析。所以下個(gè)幾十萬(wàn)上百萬(wàn)行的數(shù)據(jù)是很常見(jiàn)的。
問(wèn)題和解決方案
遇到的問(wèn)題
目前遇到的主要問(wèn)題是,隨著行為能力逐漸的完善閉環(huán),用戶數(shù)據(jù)沉淀的也越來(lái)越多了,同時(shí)業(yè)務(wù)量的也在不斷擴(kuò)大。
業(yè)務(wù)團(tuán)隊(duì)有時(shí)候會(huì)下載超量的數(shù)據(jù)來(lái)進(jìn)行分析,平臺(tái)上的數(shù)據(jù)下載能力就顯得尤為重要了。而我們的問(wèn)題是下載效率太慢,10W的數(shù)據(jù)大約要5分鐘以上才能下載下來(lái),這顯然有問(wèn)題了。
解決步驟
代碼是之前團(tuán)隊(duì)遺留的,原先功能沒(méi)開(kāi)放使用,沒(méi)有數(shù)據(jù)量,所以沒(méi)有發(fā)現(xiàn)問(wèn)題。以下是原來(lái)的導(dǎo)出模塊,原程序如下,我做了基本還原。
現(xiàn)在如何保證數(shù)據(jù)的高效導(dǎo)出是我們最重要的目標(biāo),這個(gè)也是業(yè)務(wù)團(tuán)隊(duì)最關(guān)心的。
/** * 獲取導(dǎo)出的Excel的文件流信息 * @param exportData * @return * @throws Exception */ private OutputStream getExportOutPutStream(List<UBehavDto> exportData) throws Exception { JSONObject object = new JSONObject(); List<ExcelCell[]> excelCells = new ArrayList<>(); String[] headers = new String[] { "A字段","B字段","C字段","D","E","F","G","H","I","J","K","L", "M","N","O","P","Q","R","S","T","U","V","W", "X","Y","Z","AA","AB","AC","AD","AE字段","AF字段","AG字段" }; ExcelCell[] headerRow = getHeaderRow(headers); excelCells.add(headerRow); String pattern = "yyyy-MM-dd hh:mm:ss"; for (UBehavDto uBehavDto:exportData) { String[] singleRow = new String[] { uBehavDto.getA(),uBehavDto.getB(),uBehavDto.getC(),uBehavDto.getD(),uBehavDto.getE(),uBehavDto.getF(), DateFormatUtils.format(uBehavDto.getAddTime(), pattern),DateFormatUtils.format(uBehavDto.getDate(), pattern), uBehavDto.getG(),uBehavDto.getH(),uBehavDto.getI(),uBehavDto.getJ(),uBehavDto.getK(),uBehavDto.getL(),uBehavDto.getM(), uBehavDto.getN(),uBehavDto.getO(),uBehavDto.getP(), uBehavDto.getQ(),uBehavDto.getR(),uBehavDto.getS(),String.valueOf(uBehavDto.getT()),uBehavDto.getMemo(),uBehavDto.getU(),uBehavDto.getV(), uBehavDto.getW(),uBehavDto.getX(), uBehavDto.getY(),uBehavDto.getZ(),uBehavDto.getAA(),uBehavDto.getAB(),uBehavDto.getAC() }; ExcelCell[] cells = new ExcelCell[singleRow.length]; ExcelCell getA=new ExcelCell();getA.setValue(uBehavDto.getA()); ExcelCell getB=new ExcelCell();getB.setValue(uBehavDto.getB()); ExcelCell getC=new ExcelCell();getC.setValue(uBehavDto.getC()); ExcelCell getD=new ExcelCell();getD.setValue(uBehavDto.getD()); ExcelCell getE=new ExcelCell();getE.setValue(uBehavDto.getE()); ExcelCell getF=new ExcelCell();getF.setValue(uBehavDto.getF()); ExcelCell getAddTime=new ExcelCell();getAddTime.setValue(DateFormatUtils.format(uBehavDto.getAddTime(), pattern)); ExcelCell getDate=new ExcelCell();getDate.setValue(DateFormatUtils.format(uBehavDto.getDate(), pattern)); ExcelCell getG=new ExcelCell();getG.setValue(uBehavDto.getG()); ExcelCell getH=new ExcelCell();getH.setValue(uBehavDto.getH()); ExcelCell getI=new ExcelCell();getI.setValue(uBehavDto.getI()); ExcelCell getJ=new ExcelCell();getJ.setValue(uBehavDto.getJ()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getK()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getL()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getM()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getN()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getO()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getP()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getQ()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getR()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getS()); ExcelCell a=new ExcelCell();a.setValue(String.valueOf(uBehavDto.getT())); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getMemo()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getU()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getV()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getW()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getX()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getY()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getZ()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getAA()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getAB()); ExcelCell a=new ExcelCell();a.setValue(uBehavDto.getAC()); ExcelCell[] cells = { new ExcelCell(uBehavDto.getA()), new ExcelCell().setValue(uBehavDto.getB()), new ExcelCell().setValue(uBehavDto.getC()), new ExcelCell().setValue(uBehavDto.getD()), new ExcelCell().setValue(uBehavDto.getE()), new ExcelCell().setValue(uBehavDto.getF()), new ExcelCell().setValue(DateFormatUtils.format(uBehavDto.getAddTime(), pattern)), new ExcelCell().setValue(DateFormatUtils.format(uBehavDto.getDate(), pattern)), new ExcelCell().setValue(uBehavDto.getG()), new ExcelCell().setValue(uBehavDto.getH()), new ExcelCell().setValue(uBehavDto.getI()), new ExcelCell().setValue(uBehavDto.getJ()), new ExcelCell().setValue(uBehavDto.getK()), new ExcelCell().setValue(uBehavDto.getL()), new ExcelCell().setValue(uBehavDto.getM()), new ExcelCell().setValue(uBehavDto.getN()), new ExcelCell().setValue(uBehavDto.getO()), new ExcelCell().setValue(uBehavDto.getP()), new ExcelCell().setValue(uBehavDto.getQ()), new ExcelCell().setValue(uBehavDto.getR()), new ExcelCell().setValue(uBehavDto.getS()), new ExcelCell().setValue(String.valueOf(uBehavDto.getT())), new ExcelCell().setValue(uBehavDto.getMemo()), new ExcelCell().setValue(uBehavDto.getU()), new ExcelCell().setValue(uBehavDto.getV()), new ExcelCell().setValue(uBehavDto.getW()), new ExcelCell().setValue(uBehavDto.getX()), new ExcelCell().setValue(uBehavDto.getY()), new ExcelCell().setValue(uBehavDto.getZ()), new ExcelCell().setValue(uBehavDto.getAA()), new ExcelCell().setValue(uBehavDto.getAB()), new ExcelCell().setValue(uBehavDto.getAC()) }; for(int idx=0;idx<singleRow.length;idx++) { ExcelCell cell = new ExcelCell(); cell.setValue(singleRow[idx]); cells[idx] = cell; } excelCells.add(cells); } object.put("行為數(shù)據(jù)", excelCells); ExcelUtils utils = new ExcelUtils(); OutputStream outputStream = utils.writeExcel(object); return outputStream; }
看看標(biāo)紅的代碼,這個(gè)生成Excel的方式是對(duì)Excel中的每一個(gè)cell進(jìn)行渲染,逐行的進(jìn)行數(shù)據(jù)填充,效率太慢了,根據(jù)日志分析發(fā)現(xiàn):基本時(shí)間都耗費(fèi)在數(shù)據(jù)生成Excel上。每生成1W左右的數(shù)據(jù)基本
消耗1分鐘的時(shí)間。原來(lái)在其他業(yè)務(wù)中他只是作為簡(jiǎn)量數(shù)據(jù)導(dǎo)出來(lái)使用,比如幾百條的數(shù)據(jù),很快就出來(lái)了,但是遇到大量數(shù)據(jù)導(dǎo)出的情況,性能問(wèn)題就立馬現(xiàn)形了。
團(tuán)隊(duì)內(nèi)討論了一下并參考了資料,發(fā)現(xiàn)原來(lái)業(yè)內(nèi)有很多好用強(qiáng)大的Excel處理組件,我們優(yōu)先選用阿里的easy excel來(lái)做一下嘗試。
Pom添加 easyexcel 如下:
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.4</version> </dependency>
代碼:dto內(nèi)容(中文為配置好的表頭):
package com.xxx.xxx.modules.worklog.dto; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Getter; import lombok.Setter; import java.io.Serializable; import java.util.Date; /** * <p>Description:XX表基本信息 </p> * <p>Copyright: Copyright (c) 2021 </p> * <p>Company: XX Co., Ltd. </p> * * @author brand * @date 2021-06-26 10:07:46 * <p>Update Time: </p> * <p>Updater: </p> * <p>Update Comments: </p> */ @Setter @Getter public class WorkLogDto implements Serializable { private static final long serialVersionUID = -5523294561640180605L; @ExcelProperty("A字段") private String aClolumn; @ExcelProperty("B字段") private String BColumn; @ExcelProperty("C字段") private String cColumn; @ExcelProperty("D字段") private String dColumn; @ExcelProperty("E字段") private String eColumn; @ExcelProperty("F字段") private String fColumn; @ExcelProperty("G字段") private Date gColumn; @ExcelProperty("H字段") private Date hColumn; @ExcelProperty("I字段") private String iColumn; @ExcelProperty("J字段") private String jColumn; @ExcelProperty("K字段") private String kColumn; @ExcelProperty("L字段") private String lColumn; @ExcelProperty("M字段") private String mColumn; @ExcelProperty("N字段") private String nColumn; @ExcelProperty("O字段") private String oColumn; @ExcelProperty("P字段") private String pColumn; @ExcelProperty("Q字段") private String qColumn; @ExcelProperty("R字段") private String rColumn; @ExcelProperty("S字段") private String sColumn; @ExcelProperty("T字段") private String tColumn; @ExcelProperty("U字段") private String uColumn; @ExcelProperty("V字段") private double vColumn; @ExcelProperty("W字段") private String wColumn; @ExcelProperty("X字段") private String xClumn; @ExcelProperty("Y字段") private String yColumn; @ExcelProperty("Z字段") private String zColumn; }
生成文件流的步驟(代碼很清晰了):
/** * EasyExcel 生成文件流 * @param exportData * @return */ private byte[] getEasyExcelOutPutStream(List<WorkLogDto> exportData) { try { WriteCellStyle headWriteCellStyle = new WriteCellStyle(); WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); contentWriteCellStyle.setWrapped(true); HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); EasyExcel.write(outputStream, WorkLogDto.class).sheet("行為業(yè)務(wù)數(shù)據(jù)") // Sheet名稱 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) .registerWriteHandler(horizontalCellStyleStrategy) .doWrite(exportData); byte[] bytes = outputStream.toByteArray(); // 關(guān)閉流 outputStream.close(); return bytes; } catch (Exception ex) { log.error("輸出Excel文件流失敗:"+ex.getMessage()); return null; } }
完整生成Excel文件流并上傳:
/** * 上傳用戶數(shù)據(jù)報(bào)表 * @param prmWorkLogExport * @param order * @param orderType * @return */ @Override @Async public Object uploadWorkLogData(PrmWorkLogExport prmWorkLogExport,ExportTaskDomain domain, String order, String orderType,String suid) { try { log.info(String.format("ExportWorkLog->:%s", "開(kāi)始獲取數(shù)據(jù)")); List<WorkLogDto> logList = getLogList(prmWorkLogExport,order,orderType); log.info(String.format("ExportWorkLog->:結(jié)束獲取數(shù)據(jù),總 %d 條數(shù)據(jù)", logList.size())); byte[] bytes = getEasyExcelOutPutStream(logList); log.info(String.format("ExportWorkLog->:%s","完成數(shù)據(jù)轉(zhuǎn)excel文件流")); /* 暫時(shí)作廢 Todo int max=55;int min=40; Random random = new Random(); int rd = random.nextInt(max)%(max-min+1) + min; modifyExportTask(domain.getId(),0,rd);//計(jì)算生成數(shù)據(jù)的進(jìn)度 */ //開(kāi)始投遞文件集群服務(wù)器,并將結(jié)果反寫到數(shù)據(jù)庫(kù) log.info(String.format("ExportWorkLog->:%s","開(kāi)始將數(shù)據(jù)寫入文件服務(wù)系統(tǒng)")); Dentry dentry = csService.coverUploadByByteArrayByToken(domain, bytes); //執(zhí)行異步記錄,以免連接池關(guān)閉 executor.execute(() -> { try { asynworkService.finishExportTask(domain.getId(),domain.getFileName(), dentry); } catch (Exception e) { log.error("更新任務(wù)狀態(tài)失敗:", e.getMessage()); } }); } catch (Exception ex) { // 1完成 0進(jìn)行中 2生產(chǎn)錯(cuò)誤 String updateSql = String.format(" update exporttask set statu=2 where taskid=%s;",domain.getId()); Query query = entityManager.createNativeQuery(updateSql); query.executeUpdate(); entityManager.flush(); entityManager.clear(); log.info(String.format("ExportWorkLog->:上傳文件異常:%s",ex.getMessage())); } return null; }
改用阿里 easyexcel 組件后,10W+ 的數(shù)據(jù)從生成Excel文件流到上傳只要8秒,原來(lái)約要8分鐘 ,以下為各個(gè)步驟時(shí)間點(diǎn)的日志記錄,可以看出時(shí)間消耗:
整理工具類
工具類和使用說(shuō)明
參考網(wǎng)上整理的工具類,有些類、方法在之前的版本是ok的,新版本下被標(biāo)記為過(guò)時(shí)了
package com.nd.helenlyn.common.utils; import com.alibaba.excel.EasyExcelFactory; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.metadata.BaseRowModel; import com.alibaba.excel.metadata.Sheet; import lombok.Data; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author brand * @Description: * @Copyright: Copyright (c) 2021 * @Company: XX, Inc. All Rights Reserved. * @date 2021/7/10 3:54 下午 * @Update Time: * @Updater: * @Update Comments: */ @Slf4j public class EasyExcelUtil { private static Sheet initSheet; static { initSheet = new Sheet(1, 0); initSheet.setSheetName("sheet"); //設(shè)置自適應(yīng)寬度,避免表頭重疊情況 initSheet.setAutoWidth(Boolean.TRUE); } /** * 讀取少于1000行數(shù)據(jù)的情況 * @param filePath 文件存放的絕對(duì)路徑 * @return */ public static List<Object> lessThan1000Row(String filePath){ return lessThan1000RowBySheet(filePath,null); } /** * 讀小于1000行數(shù)據(jù), 帶樣式 * filePath 文件存放的絕對(duì)路徑 * initSheet : * sheetNo: sheet頁(yè)碼,默認(rèn)為1 * headLineMun: 從第幾行開(kāi)始讀取數(shù)據(jù),默認(rèn)為0, 表示從第一行開(kāi)始讀取 * clazz: 返回?cái)?shù)據(jù)List<Object> 中Object的類名 */ public static List<Object> lessThan1000RowBySheet(String filePath, Sheet sheet){ if(!StringUtils.hasText(filePath)){ return null; } sheet = sheet != null ? sheet : initSheet; InputStream fileStream = null; try { fileStream = new FileInputStream(filePath); return EasyExcelFactory.read(fileStream, sheet); } catch (FileNotFoundException e) { log.info("找不到文件或文件路徑錯(cuò)誤, 文件:{}", filePath); }finally { try { if(fileStream != null){ fileStream.close(); } } catch (IOException e) { log.info("excel文件讀取失敗, 失敗原因:{}", e); } } return null; } /** * 讀大于1000行數(shù)據(jù) * @param filePath 文件存放的絕對(duì)路徑 * @return */ public static List<Object> mareThan1000Row(String filePath){ return moreThan1000RowBySheet(filePath,null); } /** * 讀大于1000行數(shù)據(jù), 帶樣式 * @param filePath 文件存放的絕對(duì)路徑 * @return */ public static List<Object> moreThan1000RowBySheet(String filePath, Sheet sheet){ if(!StringUtils.hasText(filePath)){ return null; } sheet = sheet != null ? sheet : initSheet; InputStream fileStream = null; try { fileStream = new FileInputStream(filePath); ExcelListener excelListener = new ExcelListener(); EasyExcelFactory.readBySax(fileStream, sheet, excelListener); return excelListener.getDatas(); } catch (FileNotFoundException e) { log.error("找不到文件或文件路徑錯(cuò)誤, 文件:{}", filePath); }finally { try { if(fileStream != null){ fileStream.close(); } } catch (IOException e) { log.error("excel文件讀取失敗, 失敗原因:{}", e); } } return null; } /** * 生成excle * @param filePath 絕對(duì)路徑, 如:/home/{user}/Downloads/123.xlsx * @param data 數(shù)據(jù)源 * @param head 表頭 */ public static void writeBySimple(String filePath, List<List<Object>> data, List<String> head){ writeSimpleBySheet(filePath,data,head,null); } /** * 生成excle * @param filePath 絕對(duì)路徑, 如:/home/{user}/Downloads/123.xlsx * @param data 數(shù)據(jù)源 * @param sheet excle頁(yè)面樣式 * @param head 表頭 */ public static void writeSimpleBySheet(String filePath, List<List<Object>> data, List<String> head, Sheet sheet){ sheet = (sheet != null) ? sheet : initSheet; if(head != null){ List<List<String>> list = new ArrayList<>(); head.forEach(h -> list.add(Collections.singletonList(h))); sheet.setHead(list); } OutputStream outputStream = null; ExcelWriter writer = null; try { outputStream = new FileOutputStream(filePath); writer = EasyExcelFactory.getWriter(outputStream); writer.write1(data,sheet); } catch (FileNotFoundException e) { log.error("找不到文件或文件路徑錯(cuò)誤, 文件:{}", filePath); }finally { try { if(writer != null){ writer.finish(); } if(outputStream != null){ outputStream.close(); } } catch (IOException e) { log.error("excel文件導(dǎo)出失敗, 失敗原因:{}", e); } } } /** * 生成excle * @param filePath 文件存放的絕對(duì)路徑, 如:/home/{user}/Downloads/123.xlsx * @param data 數(shù)據(jù)源 */ public static void writeWithTemplate(String filePath, List<? extends BaseRowModel> data){ writeWithTemplateAndSheet(filePath,data,null); } /** * 生成excle * @param filePath 文件存放的絕對(duì)路徑, 如:/home/user/Downloads/123.xlsx * @param data 數(shù)據(jù)源 * @param sheet excle頁(yè)面樣式 */ public static void writeWithTemplateAndSheet(String filePath, List<? extends BaseRowModel> data, Sheet sheet){ if(CollectionUtils.isEmpty(data)){ return; } sheet = (sheet != null) ? sheet : initSheet; sheet.setClazz(data.get(0).getClass()); OutputStream outputStream = null; ExcelWriter writer = null; try { outputStream = new FileOutputStream(filePath); writer = EasyExcelFactory.getWriter(outputStream); writer.write(data,sheet); } catch (FileNotFoundException e) { log.error("找不到文件或文件路徑錯(cuò)誤, 文件:{}", filePath); }finally { try { if(writer != null){ writer.finish(); } if(outputStream != null){ outputStream.close(); } } catch (IOException e) { log.error("excel文件導(dǎo)出失敗, 失敗原因:{}", e); } } } /** * 生成多Sheet的excle * @param filePath 絕對(duì)路徑, 如:/home/{user}/Downloads/123.xlsx * @param multipleSheelPropetys */ public static void writeWithMultipleSheel(String filePath,List<MultipleSheelPropety> multipleSheelPropetys){ if(CollectionUtils.isEmpty(multipleSheelPropetys)){ return; } OutputStream outputStream = null; ExcelWriter writer = null; try { outputStream = new FileOutputStream(filePath); writer = EasyExcelFactory.getWriter(outputStream); for (MultipleSheelPropety multipleSheelPropety : multipleSheelPropetys) { Sheet sheet = multipleSheelPropety.getSheet() != null ? multipleSheelPropety.getSheet() : initSheet; if(!CollectionUtils.isEmpty(multipleSheelPropety.getData())){ sheet.setClazz(multipleSheelPropety.getData().get(0).getClass()); } writer.write(multipleSheelPropety.getData(), sheet); } } catch (FileNotFoundException e) { log.error("找不到文件或文件路徑錯(cuò)誤, 文件:{}", filePath); }finally { try { if(writer != null){ writer.finish(); } if(outputStream != null){ outputStream.close(); } } catch (IOException e) { log.error("excel文件導(dǎo)出失敗, 失敗原因:{}", e); } } } /*********************以下為內(nèi)部類,可以提取到獨(dú)立類中******************************/ @Data public static class MultipleSheelPropety{ private List<? extends BaseRowModel> data; private Sheet sheet; } /** * 解析監(jiān)聽(tīng)器, * 每解析一行會(huì)回調(diào)invoke()方法。 * 整個(gè)excel解析結(jié)束會(huì)執(zhí)行doAfterAllAnalysed()方法 * * @author: chenmingjian * @date: 19-4-3 14:11 */ @Getter @Setter public static class ExcelListener extends AnalysisEventListener { private List<Object> datas = new ArrayList<>(); /** * 逐行解析 * object : 當(dāng)前行的數(shù)據(jù) */ @Override public void invoke(Object object, AnalysisContext context) { //當(dāng)前行 // context.getCurrentRowNum() if (object != null) { datas.add(object); } } /** * 解析完所有數(shù)據(jù)后會(huì)調(diào)用該方法 */ @Override public void doAfterAllAnalysed(AnalysisContext context) { //解析結(jié)束銷毀不用的資源 } } }
參考資料
語(yǔ)雀例子文檔:https://www.yuque.com/easyexcel/doc/easyexcel
easyexcel GitHub地址:https://github.com/alibaba/easyexcel
到此這篇關(guān)于Java大批量導(dǎo)出Excel數(shù)據(jù)的優(yōu)化過(guò)程的文章就介紹到這了,更多相關(guān)Java大批量導(dǎo)出Excel 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 使用java實(shí)現(xiàn)百萬(wàn)級(jí)別數(shù)據(jù)導(dǎo)出excel的三種方式
- Java中用POI實(shí)現(xiàn)將數(shù)據(jù)導(dǎo)出到Excel
- Java SpringMVC框架開(kāi)發(fā)之?dāng)?shù)據(jù)導(dǎo)出Excel文件格式實(shí)例詳解
- java實(shí)現(xiàn)異步導(dǎo)出數(shù)據(jù)
- Java使用easyExcel導(dǎo)出excel數(shù)據(jù)案例
- Java使用poi組件導(dǎo)出Excel格式數(shù)據(jù)
- Java使用POI導(dǎo)出大數(shù)據(jù)量Excel的方法
- java?Export大量數(shù)據(jù)導(dǎo)出和打包
相關(guān)文章
SpringBoot利用jpa連接MySQL數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了SpringBoot利用jpa連接MySQL數(shù)據(jù)庫(kù)的方法,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10java導(dǎo)出數(shù)據(jù)庫(kù)的全部表到excel
這篇文章主要為大家詳細(xì)介紹了java導(dǎo)出數(shù)據(jù)庫(kù)的全部表到excel的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03JSR303校驗(yàn)注解和自定義校驗(yàn)注解的使用
這篇文章主要介紹了JSR303校驗(yàn)注解和自定義校驗(yàn)注解的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java中時(shí)間戳的獲取和轉(zhuǎn)換的示例分析
這篇文章主要介紹了Java中時(shí)間戳的獲取和轉(zhuǎn)換的示例分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07