背景

团队目前在做一个用户数据看板(下面简称看板),基本覆盖用户的所有行为数据,并生成分析报表,用户行为由多个数据来源组成(餐饮、生活日用、充值消费、交通出行、通讯物流、交通出行、医疗保健、住房物业、运动健康...),

基于大量数据的组合、排序和统计。根据最新的统计报告,每天将近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

Java:导出Excel大批量数据的优化过程的更多相关文章

  1. java导出excel模板数据

    Java导出excel数据模板,这里直接贴代码开发,流程性的走下去就是步骤: String[] colName=new String[]{"期间","科目代码" ...

  2. java 导出Excel 大数据量,自己经验总结!

    出处: http://lyjilu.iteye.com/ 分析导出实现代码,XLSX支持: /** * 生成<span style="white-space: normal; back ...

  3. java 导出Excel 大数据量,自己经验总结!(二)

    在上一次的基础上加上了样式,以及中文列名 package com.tommy.fundation.util; import java.io.OutputStream; import java.util ...

  4. [转载]Java导出Excel

    一.需求介绍 当前B/S模式已成为应用开发的主流,而在开发企业办公系统的过程中,常常有客户这样子要求:把系统数据库中的数据导出到Excel,用户查看报表时直接用Excel打开.或者是:用户已经习惯用E ...

  5. java导出excel表格

    java导出excel表格: 1.导入jar包 <dependency> <groupId>org.apache.poi</groupId> <artifac ...

  6. java导出excel报表

    1.java导出excel报表: package cn.jcenterhome.util; import java.io.OutputStream;import java.util.List;impo ...

  7. Java导出Excel和CSV(简单Demo)

    Java导出Excel和CSV的简单实现,分别使用POI和JavaCSV. JavaBean public class ReportInfo { int id; String date; int nu ...

  8. Java 通过Xml导出Excel文件,Java Excel 导出工具类,Java导出Excel工具类

    Java 通过Xml导出Excel文件,Java Excel 导出工具类,Java导出Excel工具类 ============================== ©Copyright 蕃薯耀 20 ...

  9. java导出excel工具类

    java导出excel须要使用HSSFWorkbook这个类,须要导入poi-3.6-20091214.jar 工具类调用例如以下: package com.qlwb.business.util; i ...

随机推荐

  1. linux--------find命令之xargs

    p.p1 { margin: 0; font: 18px "Hannotate SC"; color: rgba(4, 51, 255, 1); -webkit-text-stro ...

  2. 利用bmob平台,使用云端逻辑在Xcode上实现用户注册、登录

    思路:bmob上构建云端逻辑,xcode通过http请求来在不引入bmob SDK的情况下,远程操作bmob上构建的数据库,实现注册.登录. xcode导入 AFNetWorking--------- ...

  3. MVP on Board 没用小技巧 👌

    七月入选了微软 MVP,本文记录 on board 过程中遇到的小问题和没用小技巧. MVP Portal 当你收到来自微软的确认邮件之后,你将正式被接纳为微软现任 MVP 的一员.从此刻开始,你便拥 ...

  4. 「CF576D」 Flights for Regular Customers

    「CF576D」 Flights for Regular Customers 对不起我又想网络流去了 你看这长得多像啊,走过至少多少条边就是流量下界,然后没上界 但是这个题求的最少走多少条边啊...完 ...

  5. python3安装pp过程

    并行计算的目的是将所有的核心都运行起来以提高代码的执行速度,在python中由于存在全局解释器锁(GIL)如果使用默认的python多线程进行并行计算可能会发现代码的执行速度并不会加快,甚至会比使用但 ...

  6. 微信小程序云开发-数据查询的两种写法

    从数据中查询数据有两种方法: 一.js文件的写法 1.使用传统的get方法 2.使用ES6简洁写法,推荐使用此方法  二.wxml文件的代码 把请求的数据显示在页面上.

  7. Java规范的三种注释方式:

    1.单行注释 // //单行注释 2.多行注释 /* */ /* 多行 注释 */ 3.文档注释[java特有的] /** */ ◆注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文 ...

  8. 编程思想与算法leetcode_二分算法详解

    二分算法通常用于有序序列中查找元素: 有序序列中是否存在满足某条件的元素: 有序序列中第一个满足某条件的元素的位置: 有序序列中最后一个满足某条件的元素的位置. 思路很简单,细节是魔鬼. 二分查找 一 ...

  9. 构建前端第2篇之--ESLint 配置

    张艳涛 写于2021-1-19 报错: http://eslint.org/docs/rules/space-before-function-paren Missing space before fu ...

  10. mapGetters 的作用__为什么mapGetter前面有...(三个点是去掉{}的作用)

    参考:vuex 中关于 mapGetters 的作用 mapGetters 工具函数会将 store 中的 getter 映射到局部计算属性中.它的功能和 mapState 非常类似,我们来直接看它的 ...