EasyExcel写入百万级数据到多sheet---非注解方式

左心房为你撑大大i 提交于 2019-11-29 00:29:00

EasyExcel是什么?

快速、简单避免OOM的java处理Excel工具

参考 https://segmentfault.com/a/1190000019472781,https://github.com/alibaba/easyexcel

一、项目需求

       从mongo库中查询数据,导出到excel文件中。但是动态导出的excel有多少列、列名是什么、有多少sheet页都需要动态获取。所以生成的excel也必须是动态生成,不能通过注解配置对象映射。而且写入的数据量,有可能达到100W级,使用传统的POI工具,需要把excel数据全部加载到内存空间,内存空间很容易OOM。所以选择了阿里的EasyExcel,据说可以高效的解决POI的OOM问题。

二、测试Demo

   1、引入的pom依赖

    

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>1.1.2-beta5</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>

 

   2、测试代码

package com.movitech.product.datahub.util;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.event.WriteHandler;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.Table;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.ExcelBuilderImpl;
import org.apache.poi.ss.usermodel.*;

import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author JAY
 * @Date 2019/8/29 11:00
 * @Description TODO
 **/
public class EasyExcelUtil {

    public static String excelFilePath = "C:\\Users\\lenovo\\Desktop\\Jay01-(jay01)-v5自定义导入数据.xls";

    public static void main(String[] args) {
        try {
            writeExcel(excelFilePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    public static void writeExcel(String excelFile) throws IOException {
        // 文件输出位置
        OutputStream out = new FileOutputStream(excelFile);
        ExcelWriter writer = EasyExcelFactory.getWriter(out);

        // 动态添加表头,适用一些表头动态变化的场景
        Sheet sheet1 = new Sheet(1, 0);
        sheet1.setSheetName("第一个sheet");
        // 创建一个表格,用于 Sheet 中使用
        Table table1 = new Table(1);
        // 无注解的模式,动态添加表头
        table1.setHead(createTestListStringHead());
        // 写数据
        writer.write1(new ArrayList<>(), sheet1, table1);

        // 动态添加表头,适用一些表头动态变化的场景
        Sheet sheet2 = new Sheet(2, 0);
        sheet2.setSheetName("第2个sheet");
/*
        添加TableStyle属性会使内存OOM,没办法满足分批插入100W条数据
        TableStyle tableStyle = new TableStyle();
        com.alibaba.excel.metadata.Font font = new com.alibaba.excel.metadata.Font();
        font.setBold(true);
        tableStyle.setTableContentFont(font);
        sheet2.setTableStyle(tableStyle);
*/
 
        // 创建一个表格,用于 Sheet 中使用
        Table table2 = new Table(2);
        // 无注解的模式,动态添加表头
        table2.setHead(createTestListStringHead());
        writer.write1(new ArrayList<>(), sheet2, table2);

        int x = 0;
        while (x < 1000000) {          // 模拟分批写入数据到excel,每次写入100条
            System.out.println("x = " + x);
            Table tableX = new Table(1);        // 每次从sheet的第几行开始写入
            sheet1.setStartRow(x);
            writer.write1(createDynamicModelList(x), sheet1, tableX);

            Table tableX2 = new Table(1);
            sheet2.setStartRow(x);
            writer.write1(createDynamicModelList(x), sheet2, tableX2);

            x = x + 100;
        }
        // 将上下文中的最终 outputStream 写入到指定文件中
        writer.finish();
        // 关闭流
        out.close();
    }

    private static List<List<Object>> createDynamicModelList(int x) {
        List<List<Object>> rows = new ArrayList<>();
        for (int i= x; i < 100 + x; i++){
            List<Object> row = new ArrayList<>();
            row.add("字符串-" + i);
            row.add(Long.valueOf(187837834L) + i);
            row.add(Integer.valueOf(2233 + i));
            row.add("宁-" + i);
            row.add("微信公众号: demo");
            rows.add(row);
        }

        return rows;
    }

    private static List<List<String>> createTestListStringHead() {
        // 模型上没有注解,表头数据动态传入
        List<List<String>> head = new ArrayList<List<String>>();
        List<String> headCoulumn1 = new ArrayList<String>();
        List<String> headCoulumn2 = new ArrayList<String>();
        List<String> headCoulumn3 = new ArrayList<String>();
        List<String> headCoulumn4 = new ArrayList<String>();
        List<String> headCoulumn5 = new ArrayList<String>();
        headCoulumn1.add("第1列");
        headCoulumn2.add("第2列");
        headCoulumn3.add("第3列");
        headCoulumn4.add("第4列");
        headCoulumn5.add("第5列");


        head.add(headCoulumn1);
        head.add(headCoulumn2);
        head.add(headCoulumn3);
        head.add(headCoulumn4);
        head.add(headCoulumn5);
        return head;
    }


}

   3、执行结果

 

 

 

总结:

 此测试代码可以直接运行测试查看结果。

我配置的jvm运行参数,

 

 我只给了10M空间,但是往excel中写入100W数据,程序并没有出现OOM。可以看到,使用EasyExcel,确实解决了OOM问题。

但是实际情况,EasyExcel不足以满足我的业务需求。因为除了百万级的数据导出之外,还需要进行sheet页隐藏、行隐藏、列隐藏等操作。目前EasyExcel的API,还没有那么多的功能变化。不过,easyExcel提供了自定义拦截器的功能,貌似可以给excel做样式处理。更详细的,我还没有深入研究。大致测试了一下,可以隐藏sheet,但是不知道怎么隐藏列和行。后面待解决吧

 

       

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!