zoukankan      html  css  js  c++  java
  • EasyExcel写入百万级数据到多sheet---非注解方式

    EasyExcel是什么?

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

    一、项目需求

           从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,但是不知道怎么隐藏行。测试代码如下:

     (1)隐藏列,通过自定义拦截器

    public static void writeExcelToSheet(String excelFile, Sheet sheet) throws IOException {
            // 文件输出位置
            OutputStream out = new FileOutputStream(excelFile);
            ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(null, out, ExcelTypeEnum.XLS, true, new WriteHandler() {
                @Override
                public void sheet(int i, org.apache.poi.ss.usermodel.Sheet sheet) {
                    sheet.setColumnHidden(0,true);
                    sheet.setColumnHidden(1,true);
                }
    
                @Override
                public void row(int i, Row row) {
                    System.out.println("row : " + row.getRowNum());
                }
    
                @Override
                public void cell(int i, Cell cell) {
                    System.out.println("cell : " + i);
                }
            });
    
    
            Table table1 = new Table(1);
            table1.setHead(createTestListStringHead());// 写数据
            writer.write1(createDynamicModelList(0), sheet, table1);
    
            // 将上下文中的最终 outputStream 写入到指定文件中
            writer.finish();
            // 关闭流
            out.close();
        }

      (2)隐藏sheet页,通过反射获取Workbook,用wb来设置隐藏sheet页

    /**
         * **获取workbook**
         * 因为EasyExcel这个库设计的原因
         * 只能使用反射获取workbook
         *
         * @param writer
         * @return
         */
        private static Workbook getWorkbook(ExcelWriter writer) {
            Workbook workbook = null;
            try {
                Class<?> clazz1 = Class.forName("com.alibaba.excel.ExcelWriter");
                Field[] fs = clazz1.getDeclaredFields();
                for (Field field : fs) {
                    // 要设置属性可达,不然会抛出IllegalAccessException异常
                    field.setAccessible(true);
                    if ("excelBuilder".equals(field.getName())) {
                        ExcelBuilderImpl excelBuilder = (ExcelBuilderImpl) field.get(writer);
                        Class<?> clazz2 = Class.forName("com.alibaba.excel.write.ExcelBuilderImpl");
                        Field[] fs2 = clazz2.getDeclaredFields();
                        for (Field field2 : fs2) {
                            field2.setAccessible(true);
                            if ("context".equals(field2.getName())) {
                                WriteContext context = (WriteContext) field2.get(excelBuilder);
                                workbook = context.getWorkbook();
                            }
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return workbook;
        }
    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
            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 < 10000) {
                System.out.println("x = " + x);
                Table tableX = new Table(1);
                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;
            }
    
            //获取workbook,隐藏第2页sheet
            Workbook workbook = getWorkbook(writer);
            workbook.setSheetHidden(1,true);
    
            // 将上下文中的最终 outputStream 写入到指定文件中
            writer.finish();
            // 关闭流
            out.close();
        }

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

           

  • 相关阅读:
    Ceph 知识摘录(常见故障、可用性测试)
    Ceph 知识摘录(核心组件、概要)
    Ceph 知识摘录(特点、与传统存储区别)
    服务器证书安装配置指南(IIS7.5) 分类: ASP.NET 2014-11-05 12:39 105人阅读 评论(0) 收藏
    使用JavaScriptSerializer序列化集合、字典、数组、DataTable为JSON字符串 分类: 前端 数据格式 JSON 2014-10-30 14:08 169人阅读 评论(0) 收藏
    Win7设置承载网络 分类: 网络 2014-10-30 09:08 105人阅读 评论(0) 收藏
    Http,Https (SSL)的Url绝对路径,相对路径解决方案Security Switch 4.2 中文帮助文档 分类: ASP.NET 2014-10-28 14:09 177人阅读 评论(1) 收藏
    Http,Https (SSL)的Url绝对路径,相对路径解决方案Security Switch 4.2 英文帮助文档 分类: ASP.NET 2014-10-28 10:50 147人阅读 评论(1) 收藏
    python之再学习----简单的文件
    python之再学习----简单的类(1)
  • 原文地址:https://www.cnblogs.com/ningJJ/p/11435465.html
Copyright © 2011-2022 走看看