• Java:导出Excel大批量数据的优化过程


    背景

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

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

    码字不易,欢迎关注,欢迎转载
    作者:翁智华
    本文采用「CC BY 4.0」知识共享协议进行许可,转载请注明作者及出处。
  • 相关阅读:
    拓端tecdat|R语言具有Student-t分布改进的GARCH(1,1)模型的贝叶斯估计
    拓端tecdat|R语言有极值(EVT)依赖结构的马尔可夫链(MC)对洪水极值分析
    拓端tecdat|R语言Lee-Carter模型对年死亡率建模预测预期寿命
    拓端tecdat|R语言中的模拟过程和离散化:泊松过程和维纳过程
    接口自动化文章收藏
    【转】python中获得当前目录和上级目录
    面试题
    【转】python字符串/元组/列表/字典互转
    session关联接口
    r.json()
  • 原文地址:https://www.cnblogs.com/wzh2010/p/13528320.html
Copyright © 2020-2023  润新知