1. 程式人生 > >java匯出excel 輕鬆匯出百萬條記錄

java匯出excel 輕鬆匯出百萬條記錄

網上查了很多部落格,發現很多匯出excel 都是 存一個集合 然後利用poi 的api 匯出 ,這樣在資料量比較小的時候沒什麼問題,但是在資料量稍微大一點就會造成堆疊溢位了。所以寫了一個每次匯出一條記錄的工具類,分享給大家!完美匯出超大資料集合。不多說 ,直接貼程式碼

首先是一個註解類
package com.cmi.jego.activity.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import
java.lang.annotation.Target; /** * Created by wl */ @Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ExportAnnotation { int order() default 100;//排序(屬性對應excel的列 的列數) String method() default "";//屬性對應的get方法 //匯出title String columnTitle() default
"";//屬性對應的excel 的title //title陣列長度 int length() default 20;//列的個數 }

匯出的實體物件 javaBean(例子)

package com.cmi.jego.activity.vo;

import com.cmi.jego.activity.annotation.ExportAnnotation;

/**
 * Created by wl
 */
@ExportAnnotation(length = 9)
public class RedeemCodeExportVO {

    @ExportAnnotation
(order = 1,method = "getCouponId",columnTitle = "優惠券id") private String couponId; @ExportAnnotation(order = 2,method = "getCouponName",columnTitle = "優惠券名稱") private String couponName; @ExportAnnotation(order = 3,method = "getExchangeCode",columnTitle = "兌換碼") private String exchangeCode; @ExportAnnotation(order = 4,method = "getCommodityName",columnTitle = "商品名稱") private String commodityName; @ExportAnnotation(order = 5,method = "getCommodityIds",columnTitle = "商品id") private String commodityIds; @ExportAnnotation(order = 6,method = "getCreateDate",columnTitle = "生成日期") private String createDate; @ExportAnnotation(order = 7,method = "getNoEffectDate",columnTitle = "失效日期") private String noEffectDate; @ExportAnnotation(order = 8,method = "getStatus",columnTitle = "兌換狀態") private String status; @ExportAnnotation(order = 9,method = "getExchangeMobile",columnTitle = "兌換手機號") private String exchangeMobile; public String getCouponId() { return couponId; } public void setCouponId(String couponId) { this.couponId = couponId; } public String getCouponName() { return couponName; } public void setCouponName(String couponName) { this.couponName = couponName; } public String getExchangeCode() { return exchangeCode; } public void setExchangeCode(String exchangeCode) { this.exchangeCode = exchangeCode; } public String getCommodityName() { return commodityName; } public void setCommodityName(String commodityName) { this.commodityName = commodityName; } public String getCommodityIds() { return commodityIds; } public void setCommodityIds(String commodityIds) { this.commodityIds = commodityIds; } public String getCreateDate() { return createDate; } public void setCreateDate(String createDate) { this.createDate = createDate; } public String getNoEffectDate() { return noEffectDate; } public void setNoEffectDate(String noEffectDate) { this.noEffectDate = noEffectDate; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getExchangeMobile() { return exchangeMobile; } public void setExchangeMobile(String exchangeMobile) { this.exchangeMobile = exchangeMobile; } }

order 是從1開始的因為專案的需求 第一列都是序號

真正的工具類

package com.cmi.jego.activity.utils;

import com.cmi.jego.activity.annotation.ExportAnnotation;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by wl
 */
public class ExportExcelUtil{

    private static final Logger logger = LoggerFactory.getLogger(ExportExcel.class);
    //200rows flush
    private final SXSSFWorkbook wbk = new SXSSFWorkbook(200);
    //工作簿 當前sheet
    private SXSSFSheet sheet;
    //當前sheet的 當前row
    private SXSSFRow row;        //成員變數接收  不用每次都在棧中宣告
    //當前cell
    private SXSSFCell cell;
    //標題樣式    初始化 分配一個堆記憶體空間
    private final XSSFCellStyle titleStyle = (XSSFCellStyle) this.wbk.createCellStyle();
    //cell樣式
    private final XSSFCellStyle cellStyle = (XSSFCellStyle) this.wbk.createCellStyle();
    //row 樣式
   // private final XSSFCellStyle rowStyle = (XSSFCellStyle) this.wbk.createCellStyle();
    //cell font
//    private final Font font = this.wbk.createFont();
////    使用太耗記憶體
//    private XSSFRichTextString xssfRichTextString;

    //接收cellValue  當前cellValue
    private Object cellValue;

    //標題[0][]   方法名[1][]
    private final String[][] titlesAndMethods = new String[2][];

    //匯出物件的 Class 物件    使用泛型獲取匯出物件的型別 只能獲取父類的泛型的型別  這裡通過構造器初始化
    //Class.forName(((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName());
    private final Class<?> exportClass;

    public ExportExcelUtil(Class<?> clazz){
        this.exportClass = clazz;
        if(clazz == null)
            throw new NullPointerException("clazz 不能為空");
        this.initTitleAndMethod(this.exportClass);

    }


    //初始化 樣式
    {
        this.initStyle(this.titleStyle,false);
        this.initStyle(this.cellStyle,true);
        //this.initStyle(rowStyle,true);
    }


    private int count = 1;

    public void exportRowSet(Object rowData) {
        if(this.sheet ==null){
            throw new NullPointerException("sheet 為空 , 先initSheet");
        }

        if(this.count % 10000 == 0){
            logger.info("=========="+Runtime.getRuntime().totalMemory()/1024/1024+"M");
            //System.runFinalization();
        }
        this.row = this.sheet.createRow(count);
        //第一個cell 是序號
        {
            this.cell = this.row.createCell(0);
            this.cell.setCellStyle(cellStyle);
            this.cell.setCellValue(count);
        }

        for(int i=1;i<titlesAndMethods[0].length && titlesAndMethods[1][i] != null;i++) {
            try {
                this.cellValue = this.exportClass.getMethod(this.titlesAndMethods[1][i]).invoke(rowData);
                this.cell = this.row.createCell(i);
                this.createCellValue(i);

            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                //logger.error("匯出統計資料 反射獲取值 失敗,column:{}",i,e);
            }

        }
        this.count++;
    }

    public void writeWbk(OutputStream os) {
        try {
            this.wbk.write(os);
        } catch (IOException e) {
           //
        }finally {
            try {
                this.wbk.close();
            } catch (IOException e) {
                //
            }
        }
    }

    public void initSheet(String sheetName){
        this.sheet = this.wbk.createSheet(sheetName);
        this.sheet.setDefaultColumnWidth((short) 16);
        this.row = this.sheet.createRow(0);
        this.createRowTitle();
        this.count = 1;
    }

    private void initTitleAndMethod(Class<?> clazz) {
        ExportAnnotation exportAnnotation =  clazz.getAnnotation(ExportAnnotation.class);
        Field[] fields = clazz.getDeclaredFields();
        //初始化 標題[0][0]
        {
            //第一個title 序號
            int length = exportAnnotation.length();
            this.titlesAndMethods[0] = new String[length+1];
            this.titlesAndMethods[1] = new String[length+1];
            this.titlesAndMethods[0][0] = "序號";
        }
        int index;
        for(Field field :fields){
            exportAnnotation = field.getAnnotation(ExportAnnotation.class);
            if(exportAnnotation != null) {
                index = exportAnnotation.order();
                //標題
                this.titlesAndMethods[0][index] = exportAnnotation.columnTitle();
                this.titlesAndMethods[1][index] = exportAnnotation.method();
            }
        }
    }

    /**
     * 建立標題行
     */
    private  void createRowTitle() {
        String[] titles = titlesAndMethods[0];
        for(int i=0;i<titles.length;i++){
            this.cell = this.row.createCell(i);
            this.cell.setCellStyle(this.titleStyle);
            //每次都要例項化一個  如果為final cellValue 全部相同    太耗記憶體
//            this.xssfRichTextString = new XSSFRichTextString();
//            this.xssfRichTextString.setString(titles[i]);
            this.cell.setCellValue(titles[i]);
        }
    }

    private void initStyle(XSSFCellStyle style,boolean isBold) {
        // 設定這些樣式SOLID_FOREGROUND
        style.setFillForegroundColor(HSSFColor.WHITE.index);
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setBorderTop(BorderStyle.THIN);
        style.setAlignment(HorizontalAlignment.CENTER);
        // 生成一個字型
        XSSFFont font = (XSSFFont) this.wbk.createFont();
        font.setColor(HSSFColor.BLACK.index);
        font.setFontHeightInPoints((short) 12);
        font.setBold(isBold);
        style.setFont(font);

    }

    /**
     * 建立 cell
     */
    private void createCellValue(int columnIndex){

        this.cell.setCellStyle(this.cellStyle);
        // 判斷值的型別後進行強制型別轉換
        String textValue = null;
        if (this.cellValue instanceof Date) {
            Date date = (Date) this.cellValue;
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            textValue = sdf.format(date);
        }
        else if (this.cellValue instanceof byte[]) {
            Drawing patriarch = this.sheet.createDrawingPatriarch();
            // 有圖片時,設定行高為60px;
            this.row.setHeightInPoints(60);
            // 設定圖片所在列寬度為80px,注意這裡單位的一個換算
            this.sheet.setColumnWidth(columnIndex, (short) (35.7 * 80));
            // sheet.autoSizeColumn(i);
            byte[] bsValue = (byte[]) this.cellValue;
            ClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 255, (short) 6, columnIndex, (short) 6, columnIndex);
            anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE);
            patriarch.createPicture(anchor, this.wbk.addPicture(bsValue, HSSFWorkbook.PICTURE_TYPE_JPEG));
        }
        else {
            // 其它資料型別都當作字串簡單處理
            if (this.cellValue != null) textValue = this.cellValue.toString();
        }
        // 如果不是圖片資料,就判斷textValue是否全部由數字組成
        if (textValue != null) {
            if (this.cellValue instanceof Integer) {
                //double 強制轉換 會有小數點
                this.cell.setCellValue((int) this.cellValue);
            }else if(this.cellValue instanceof Number){
                this.cell.setCellValue((double) this.cellValue);
            }
            else {
                // 資料量大每次例項化 太耗記憶體
//                this.xssfRichTextString = new XSSFRichTextString();
//                this.xssfRichTextString.setString(textValue);
//                this.font.setColor(new XSSFColor().getIndexed());
//                this.xssfRichTextString.applyFont(font);
                this.cell.setCellValue(textValue);
            }
        }
    }

}

工具類中的titleAndMethods[][] 是一個二維陣列titleAndMethods[0]用來存放title titleAndMethods[1]用來存放get方法 兩個長度是一樣的。 在ExportUtil初始化的時候 通過反射填充
注意 :這裡我的excel 匯出的第一列固定是“序號”如果你的第一列不是序號請在匯出的javaBean 屬性中新增一個 order=0的屬性 並增加title 和method 註解,並且將工具類做如下修改
註釋掉下面的程式碼

this.titlesAndMethods[0][0] = "序號";

//第一個cell 是序號
{
    this.cell = this.row.createCell(0);
    this.cell.setCellStyle(cellStyle);
    this.cell.setCellValue(count);
}

迴圈從0開始

 for(int i=0;i<titlesAndMethods[0].length && titlesAndMethods[1][i] != null;i++) {
            try {
                this.cellValue = this.exportClass.getMethod(this.titlesAndMethods[1][i]).invoke(rowData);
                this.cell = this.row.createCell(i);
                this.createCellValue(i);

            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                //logger.error("匯出統計資料 反射獲取值 失敗,column:{}",i,e);
            }

        }

使用該工具類共分為四部

  1. 例項化ExportUtil 注意構造器中的Class 引數 必須要有ExportAnnotation註解 且註解正確 ExportUtil exportUtil = new ExportUtil(RedeemCodeExportVO.class)
  2. 初始化sheet exportUtil.initSheet(“sheetName”);
  3. 匯出一個結果集 exportUtil.exportRowSet(redeemCodeExportVO) 匯出物件可能需要封裝
  4. 寫 exportUtil.writeWbk(os);

下面是一個匯出資料庫資料的例子使用的是jdbc 查詢每次匯出一個結果集 避免全部查詢儲存在匯出的高記憶體消耗
匯出流程大概是

  1. 建立檔案
  2. sql查詢(封裝jdbc 每次匯出一個封裝好的結果集)
  3. 上傳檔案到oss伺服器
  4. 將oss下載key返回瀏覽器

    建立一個模板 程式碼如下(寫在內部類中)

 //匯出模板 =============================================
    //匯出步驟
    //1.例項化ExportUtil ExportUtil exportUtil = new ExportUtil(clazz);
    //2.呼叫匯出一個結果集方法 (封裝ResultSet)       模板 回撥
    //3.寫出輸出流  exportUtil.write(os)            模板 回撥
    //4.上傳到oss                                  模板 回撥
    //5.返回oss key到前端 
    //6.前端通過 oss key 請求後端下載檔案
    private static class ExportTemplate {
        //excel 寫入的檔案
        private final File file;
        //匯出結果返回的物件
        ExportStatisticResp exportStatisticResp;
        //例項化 file
        private ExportTemplate(String filePath, String fileName,ExportStatisticResp exportStatisticResp){
            File pathFile = new File(filePath);
            if (!mkdirs(pathFile)) {
                logger.info("建立臨時目錄失敗,使用當前目錄");
                file = new File(fileName);
            } else {
                file = new File(filePath, fileName);
            }
            this.exportStatisticResp = exportStatisticResp;
        }
        private ExportStatisticResp executeExport(ExportCallBackHandler exportCallBackHandler) {
            OutputStream os = null;
            try {
                if (file == null)
                    throw new NullPointerException("file 不能為空");
                //doSomething
                os = new BufferedOutputStream(new FileOutputStream(file));
                //回撥
                return exportCallBackHandler.processExport(os,file,exportStatisticResp);
               //doSomething
            } catch (ParseException | IOException | RuntimeException e) {
                logger.error(e.getMessage(),e);

            } finally {
                if(file != null) {
                    boolean isDeleted = file.delete();
                    if (!isDeleted)
                        logger.info("刪除臨時檔案失敗");
                }
                if(os != null){
                    try {
                        os.close();
                    } catch (IOException e) {
                       logger.info("匯出 輸出流關閉異常",e);
                    }
                }
            }
            //失敗
            exportStatisticResp.setStatus(ExportStatisticResp.StatusEnum.Failed.status);
            exportStatisticResp.setMsg("匯出失敗");
            return exportStatisticResp;
        }

        private boolean mkdirs(File file) {
            return file.exists() || file.mkdirs();
        }

        //回撥介面
        private interface ExportCallBackHandler {
            // os  輸出流 到file             file   oss上傳的檔案     返回值 匯出物件
            ExportStatisticResp processExport(OutputStream os,File file,ExportStatisticResp exportStatisticResp) throws ParseException, IOException;
        }
    }

ExportStatisticResp 是我定義的一個返回物件 大家可以自己定義

JDBC 使用的是spring 的JdbcTemplate

@Autowired
private JdbcTemplate jdbcTemplate;

封裝查詢sql程式碼如下

/**
     * 通過 MyRowCallBackHandler 處理結果集 防止查出所有資料記憶體溢位
     */
    private void exportRedeemResultSet(RedeemCodeQueryVO queryVO, Map<String, ActivityInfoGiftVO> map, ExportExcelUtil exportExcelUtil) {
        //拼裝sql
        String sql = ObjectUtil.getRedeemCodeQuerySql(queryVO);
        logger.info("sql:{}", sql);
        //每次查10000個結果集
        jdbcTemplate.setFetchSize(10000);
        jdbcTemplate.query(sql, new MyRowCallBackHandler(map, exportExcelUtil));
    }

    /**
     * 匯出一個結果集
     */
    private static class MyRowCallBackHandler implements RowCallbackHandler {
        private final Map<String, ActivityInfoGiftVO> map;
        private final ExportExcelUtil exportExcelUtil;
        private final RedeemCodeExportVO rowData = new RedeemCodeExportVO();

        private MyRowCallBackHandler(Map<String, ActivityInfoGiftVO> map, ExportExcelUtil exportExcelUtil) {
            this.map = map;
            this.exportExcelUtil = exportExcelUtil;
            exportExcelUtil.initSheet("yyh");
        }

        @Override
        public void processRow(ResultSet rs) throws SQLException {
            //封裝 RedeemCodeExportVO
            ObjectUtil.fillRedeemCodeExportVO(map, rs, rowData);
            exportExcelUtil.exportRowSet(rowData);
        }
    }

自定義一個 MyRowCallBackHandler 實現 JDBC 的RowCallbackHandler 介面 在回掉方法public void processRow(ResultSet rs)中封裝 匯出的物件 並且呼叫exportExcelUtil.exportRowSet(rowData);

在自定義MyRowCallBackHandler 類中還有一些成員變數 因為sql查詢出來的欄位可能不能完全滿足匯出的所有欄位要求,還需要外部的一些資料進行封裝 ,另外就是ExportExcelUtil物件由外層呼叫方法傳遞過來 (注意要保證一個請求只例項化一個ExportExcelUtil物件 )在構造器中新增引用
private final RedeemCodeExportVO rowData = new RedeemCodeExportVO(); 是一個final 的例項 是因為匯出的是rowData的資料 不用每次都例項化,只需要在裡面填充資料。

下面是接收請求的方法 程式碼如下

@RequestMapping("/exportCode")
    public ResMesg exportCode(RedeemCodeQueryVO queryVO, HttpServletRequest request) {
        ExportStatisticResp exportStatisticResp = new ExportStatisticResp();

        if (queryVO.getActivityId() != null) {

            List<ActivityInfoGiftVO> giftVOs = iActivityMicroService.findActivityInfoGiftListByActivityInfoId(queryVO.getActivityId());
            //需要到processRow中封裝的資料(sql查詢出來的資料不滿足匯出要求  聯合查詢會降低效率)
            Map<String, ActivityInfoGiftVO> map = new HashMap<>();
            convertList2Map(giftVOs, map);
            //檔名
            String fileName = "fileName";
            //檔案路徑
            String filePath = request.getServletContext().getRealPath("/") + "/temp/";
            //上傳的oss  key
            String exportOssKey = "activity/exportRedeem/" + fileName;
            //模板模式
            return new ExportTemplate(filePath, fileName, exportStatisticResp).executeExport(new ExportTemplate.ExportCallBackHandler() {
                @Override
                public ExportStatisticResp processExport(OutputStream os, File file, ExportStatisticResp exportStatisticResp) throws ParseException, IOException {
//回撥介面
                    //例項化匯出工具
                    ExportExcelUtil exportExcelUtil = new ExportExcelUtil(RedeemCodeExportVO.class);
                    //執行RowCallBackHandler 回掉介面 processRow
                    exportRedeemResultSet(queryVO, map, exportExcelUtil);
                    exportExcelUtil.writeWbk(os);
                    //上傳檔案到oss
                    ossFileService.putObject("xlsx", exportOssKey, file.length(), new BufferedInputStream(new FileInputStream(file)));

                   //封裝返回物件 exportStatisticResp.setStatus(ExportStatisticResp.StatusEnum.Success.status);
                    exportStatisticResp.setMsg("匯出成功");
                    exportStatisticResp.setOsskey(exportOssKey);
                    exportStatisticResp.setFileName(fileName);
                    logger.info("匯出地址=======>>>" + accessService.oss + exportOssKey);
                    return exportStatisticResp;

                }
            });
        }

        exportStatisticResp.setStatus(ExportStatisticResp.StatusEnum.SuccessWithoutData.status);
        exportStatisticResp.setMsg("沒有查詢到相應資料");
        return exportStatisticResp;
    }

前端接收到oss的key在請求後端下載檔案

每一個匯出請求都可以按照上面的模式進行處理

下面貼一下直接匯出一個excel檔案的測試程式碼(每一萬行列印使用的記憶體 測試10萬 50萬 100萬 200萬)
程式碼如下(資料量自己設定)

public static void main(String[] args) throws Exception{
      System.out.println("=========================>>>>>" + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "M");
      long start = System.currentTimeMillis();
      ExportExcelUtil exportExcelUtil = new ExportExcelUtil(RedeemCodeExportVO.class);
      exportExcelUtil.initSheet("abcd");
      RedeemCodeExportVO redeemCodeExportVO;
      OutputStream os = new BufferedOutputStream(new FileOutputStream(new File("1234.xlsx")));
      for(int i=0;i<100000;i++){
          redeemCodeExportVO = new RedeemCodeExportVO();
          redeemCodeExportVO.setExchangeMobile(i+"");
          redeemCodeExportVO.setStatus("a" + i);
          redeemCodeExportVO.setNoEffectDate("b"+i);
          redeemCodeExportVO.setCreateDate("c" + i);
          redeemCodeExportVO.setCommodityName("d"+i);
          redeemCodeExportVO.setCommodityIds("e" +i);
          redeemCodeExportVO.setExchangeCode("f"+i);
          redeemCodeExportVO.setCouponName("h"+i);
          redeemCodeExportVO.setCouponId("g"+i);
          exportExcelUtil.exportRowSet(redeemCodeExportVO);
      }
      exportExcelUtil.writeWbk(os);
      long end = System.currentTimeMillis();
      System.out.println("================================>>" +(end - start) +"ms");

  }

十萬

=========================>>>>>121M
03:16:29.555 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========121M
03:16:29.714 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========153M
03:16:29.857 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========154M
03:16:29.963 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========218M
03:16:30.075 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========218M
03:16:30.175 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========219M
03:16:30.345 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========296M
03:16:30.485 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========296M
03:16:30.641 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========296M
03:16:30.779 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========296M
================================>>7363ms

50萬

=========================>>>>>121M
03:20:39.931 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========153M
03:20:40.185 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========153M
03:20:40.311 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========218M
03:20:40.420 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========218M
03:20:40.528 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========218M
03:20:40.622 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========218M
03:20:40.750 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========346M
03:20:40.892 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========346M
03:20:41.004 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========346M
03:20:41.113 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========347M
03:20:41.222 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========347M
03:20:41.315 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========347M
03:20:41.404 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========347M
03:20:41.509 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:41.633 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:41.721 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:41.810 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:41.913 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:42.027 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:42.129 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:42.230 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:42.352 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:42.456 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:42.551 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:42.666 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========501M
03:20:42.761 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========475M
03:20:42.849 [main] INFO com.cmi.jego.activity.utils.ExportExcel - ==========475M
03:20:42.946 [main] INF