使用EasyExcel导出百万条数据

news2024/11/20 1:51:28

使用EasyExcel导出百万条数据

应用是基于100W条数据进行的测试
首先:导入相关需要的依赖:

		<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.16</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.16</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.10</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml-schemas</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>

        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency>
         

创建所需要的实体类,如下:

@Accessors(chain = true)
@Data
public class ExcelBean {

    @ExcelProperty("主键id")
    private String id;

    @ExcelProperty("姓名")
    private String name;

    @ExcelProperty("地址")
    private String address;

    @ExcelProperty("年龄")
    private Integer age;

    @ExcelProperty("数量")
    private Integer number;

    @NumberFormat("#.##")
    @ExcelProperty("身高")
    private Double high;

    @ExcelProperty("距离")
    private Double distance;

    @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
    @ExcelProperty("开始时间")
    private Date startTime;

    @ExcelProperty("结束时间")
    private Date endTime;
}

创建所用到的测试类,如下:

@Slf4j
public class writeExcelByApi {

    public static final String FILE_NAME = "C:\\Users\\861123001\\Desktop\\mqtt压测软件\\自造数据\\test_04.xlsx";
    // 每个 sheet 写入的数据
    public static final int NUM_PER_SHEET = 300000;
    // 每次向 sheet 中写入的数据(分页写入)
    public static final int NUM_BY_TIMES = 50000;

    @Test
    public void writeExcelByApi(){
        String fileName = FILE_NAME;
        log.info("导出excel名称={}",fileName);
        long startTime = System.currentTimeMillis();
        //调用api
        List<ExcelBean> date = getDate();
        EasyExcel.write(fileName,ExcelBean.class).sheet().doWrite(date);
        log.info("导出excel结束,数据量={},耗时={}ms", date.size(), System.currentTimeMillis() - startTime);
    }

    /**
     * 获取excel 导出的数据
     *
     * @return list 集合
     */
    public static List<ExcelBean> getDate(){
        log.info("开始生成数据");
        //创建返回数据集合
        List<ExcelBean> list = new ArrayList<>();
        Date date = new Date();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 2000000; i++) {
            //创建数据对象
            ExcelBean excelBean = new ExcelBean();
            ExcelBean excel = excelBean.setId(UUID.randomUUID().toString())
                    .setName("小明" + (10000 + i))
                    .setAddress("浙江省杭州市西湖")
                    .setAge(i)
                    .setNumber(i + 10000)
                    .setHigh(1.234 * i)
                    .setDistance(3.14 * i)
                    .setStartTime(date)
                    .setEndTime(date);

            list.add(excel);
        }
        log.info("数据生成结束,数据量={},耗时={}ms", list.size(), System.currentTimeMillis() - startTime);
        return list;
    }
}

EasyExcel 导出 excel 应用优化一:可以通过分sheet来解决超出100万的数据

	@Test
    public void writeExcelByMulSheet() {
        String fileName = FILE_NAME;
        log.info("导出excel名称={}",fileName);
        long startTime = System.currentTimeMillis();

        //获取数据
        List<ExcelBean> date = getDate();

        //获取sheet的个数
        int sheetNum = date.size() % NUM_PER_SHEET == 0 ? date.size() / NUM_PER_SHEET : date.size() / NUM_PER_SHEET + 1;

        //指定写入的文件
        ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelBean.class).build();
        for (int i = 0; i < sheetNum; i++) {
            long l = System.currentTimeMillis();

            //设置sheet的名字,每个sheet名称不能相同
            String sheetName = "sheet" + i;
            WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetName).build();

            //开始根结束行数
            int startNum = i * NUM_PER_SHEET;
            int endNum = i == sheetNum - 1 ? date.size() : (i + 1) *  NUM_PER_SHEET;

            excelWriter.write(date.subList(startNum, endNum), writeSheet);
            log.info("写入sheet={},数据量{}-{}={},耗时={}ms", sheetName, endNum, startNum, endNum - startNum, System.currentTimeMillis() - l);
        }
        //最好放在finally中
        excelWriter.finish();
        log.info("导出excel结束,总数据量={},耗时={}ms", date.size(), System.currentTimeMillis() - startTime);
    }

EasyExcel 导出 excel 应用优化二:数据源 list 太大,直接读取全部的 list 数据导致 OOM
将 list 数据进行分页读取,并进行分页写入到 excel。这样还有个好处,每次每页读取部分数据,然后写入到 excel 中(相当于该批数据已经从内存刷到了磁盘),也增加了写入的效率;poi 中的导出excel,为此专门提供了一个刷新磁盘的 api,具体代码如下

	@Test
    public void writeExcelByMulWrite() {
        String fileName = FILE_NAME;
        log.info("导出excel名称={}",fileName);
        long startTime = System.currentTimeMillis();

        //获取数据
        List<ExcelBean> date = getDate();
        ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelBean.class).build();

        //适用于针对100万数据以下的写法
        //WriteSheet writeSheet = EasyExcel.writerSheet("testSheet" ).build();

        //计算需要写的次数
        int times = date.size() % NUM_BY_TIMES == 0 ? date.size() / NUM_BY_TIMES : date.size() / NUM_BY_TIMES + 1;
        for (int i = 0; i < times; i++) {
            long l = System.currentTimeMillis();
            WriteSheet writeSheet = EasyExcel.writerSheet("testSheet" + i).build();
            //开始跟结束行数
            int startNum = i * NUM_BY_TIMES;
            int endNum = i == times - 1 ? date.size() : (i + 1) * NUM_BY_TIMES;

            excelWriter.write(date.subList(startNum, endNum), writeSheet);
            log.info("写入数量{}-{}={},耗时={}ms", endNum, startNum, endNum - startNum, startTime - l);
        }
        //最好写在finally里
        if (excelWriter != null) {
            excelWriter.finish();
        }
    }

EasyExcel 导出 excel 应用优化三:结合前面两种方案
将 list 数据进行分页读取,并且每个 sheet 分多次写入,且写入到多个 sheet 中

	@Test
    public void writeExcelByMulSheetAndMulWrite() {
        String fileName = FILE_NAME;
        log.info("导出excel名称={}", fileName);
        long startTime = System.currentTimeMillis();

        //获取数据
        List<ExcelBean> date = getDate();

        //获取sheet表数
        int sheetNum = date.size() % NUM_PER_SHEET == 0 ? date.size() / NUM_PER_SHEET : date.size() / NUM_PER_SHEET + 1;

        //获取每个sheet导入的次数
        int writeNumPerSheet = NUM_PER_SHEET % NUM_BY_TIMES == 0 ? NUM_PER_SHEET / NUM_BY_TIMES : NUM_PER_SHEET / NUM_BY_TIMES + 1;

        // 最后一个 sheet 写入的数量
        int writeNumLastSheet = date.size() - (sheetNum - 1) * NUM_PER_SHEET;

        // 最后一个 sheet 写入的次数
        int writeNumPerLastSheet = writeNumLastSheet % NUM_BY_TIMES == 0 ? writeNumLastSheet / NUM_BY_TIMES : writeNumLastSheet / NUM_BY_TIMES + 1;

        // 指定写入的文件
        ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelBean.class).build();

        for (int i = 0; i < sheetNum; i++) {
            String sheetName = "sheet" + i;
            WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetName).build();
            int writeNum = i == sheetNum - 1 ? writeNumPerLastSheet : writeNumPerSheet; // 每个sheet 写入的次数
            int endEndNum = i == sheetNum - 1 ? date.size() : (i + 1) * NUM_PER_SHEET; // 每个sheet 最后一次写入的最后行数

            for (int j = 0; j < writeNum; j++) {
                long l = System.currentTimeMillis();
                int startNum = i * NUM_PER_SHEET + j * NUM_BY_TIMES;
                int endNum = j == writeNum - 1 ? endEndNum : i * NUM_PER_SHEET + (j + 1) * NUM_BY_TIMES;
                excelWriter.write(date.subList(startNum, endNum), writeSheet);
                log.info("写入sheet={},数据量={}-{}={},耗时={}", sheetName, endNum, startNum, endNum - startNum, startTime - l);
            }
        }
        // 需要放入 finally 中
        if (excelWriter != null) {
            excelWriter.finish();
        }
        log.info("导出excel结束,总数据量={},耗时={}ms", date.size(), System.currentTimeMillis() - startTime);

    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1358527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈园区建设“一站式企业服务平台”的必要性!

​ 随着数字经济的快速发展与新一代信息技术的日新月异&#xff0c;打造智慧型、高效能的园区运营模式已成为现代产业园区转型升级的关键路径&#xff0c;其中&#xff0c;构建“一站式企业服务平台”成为了园区创新企业服务机制、提升企业服务效能、优化营商环境的重要举措。 …

得物商品状态体系介绍

一、得物的商品体系 目前得物的商品分为三种类型&#xff0c;分别是&#xff1a;新品、商品、草稿。但是只有商品是可售卖的&#xff0c;新品和草稿都不是可售卖的。 新品有很多种创建的渠道&#xff0c;商品可以由新品选品通过后由系统自动生成&#xff0c;也可以由运营直接…

QT工具栏开始,退出

QT工具栏开始&#xff0c;退出 //初始化场景QMenuBar *bar menuBar();setMenuBar(bar);QMenu *startbar bar->addMenu("开始");QAction * quitAction startbar->addAction("退出");connect(quitAction , &QAction::triggered,[](){this->c…

【linux】线程同步+基于BlockingQueue的生产者消费者模型

线程同步基于BlockingQueue的生产者消费者模型 1.线程同步2.生产者消费者模型3.基于BlockingQueue的生产者消费者模型 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.线程同步 在线程互斥写了一份抢票的代码&#xff0c;我们发现虽然加锁解决了抢到负数票的…

面试题:Spring Boot 中如何统计代码执行耗时

文章目录 ① StopWatch② System.nanoTime()③ new Date()④ System.currentTimeMillis() 开始 System.currentTimeMillis() 减去 结束 System.currentTimeMillis() 等于 耗时 其实我个人感觉OK的&#xff0c;就这样就蛮好的&#xff0c;很多项目都是这样用的。 简简单单的挺…

基于Java SSM框架实现游戏论坛平台系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现游戏论坛平台系统演示 摘要 本论文主要论述了如何使用java语言开发一个游戏论坛平台的设计&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构、ssm 框架和 java 开发的 Web 框架&#xff0c;基于Werkzeug WSGI工具箱和…

2024.1.5每日一题

LeetCode每日一题 1944.队列中可以看到的人数 1944. 队列中可以看到的人数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 有 n 个人排成一个队列&#xff0c;从左到右 编号为 0 到 n - 1 。给你以一个整数数组 heights &#xff0c;每个整数 互不相同&#xff0c;heig…

将 validator 校验器从 ParameterValidator 中抽离出来

目录 一、前置说明1、总体目录2、相关回顾3、本节目标 二、操作步骤1、项目目录2、代码实现3、测试代码4、日志输出 三、后置说明1、要点小结2、下节准备 一、前置说明 1、总体目录 《 pyparamvalidate 参数校验器&#xff0c;从编码到发布全过程》 2、相关回顾 pyparamval…

科研上新 | 第6期:优化LLM数学推理;深度学习建模基因表达调控;基于深度学习的近实时海洋碳汇估算

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 本期内容速览 …

【深度学习:SENet】信道注意力和挤压激励网络(SENet):图像识别的新突破

【深度学习&#xff1a;SENet】信道注意力和挤压激励网络&#xff08;SENet&#xff09;&#xff1a;图像识别的新突破 为什么有效如何实现工作原理应用案例 挤压和激励网络&#xff08;SENets&#xff09;为卷积神经网络&#xff08;CNN&#xff09;引入了一个新的构建模块&am…

centos 7.9安装RocketMQ4.6.1版本

1.先下载二进制文件 下载 | RocketMQ 2.下载后&#xff0c;进行解压 unzip rocketmq-all-4.6.1-bin-release.zip 3.修改JVM配置 进到/datadrive/rocketmq-all-4.6.1-bin-release/bin下编辑runserver.sh 与 runbroker.sh文件 根据个人虚拟机大小进行修改 vi runserver.sh J…

这个应该是全网最全的接口测试工具之postman

概念 接口测试是什么&#xff1f; 百度百科给出的解释是&#xff1a; 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间…

下载知虾数据分析软件:优化店铺运营、提高转化率的利器

在如今竞争激烈的电商市场中&#xff0c;对销售数据、流量以及买家行为等关键指标的监控和分析至关重要。Shopee平台为卖家提供了一个内置的在线数据分析工具——“Shopee Analytics”&#xff08;知虾分析&#xff09;&#xff0c;让卖家能够轻松实现对店铺运营的优化、提高转…

40 个简单又有效的 Linux Shell 脚本示例

【收藏】Linux系统常用命令速查手册&#xff08;附PDF下载方式&#xff09;_linux命令大全详解pdf-CSDN博客 历史上&#xff0c;shell 一直是类 Unix 系统的本地命令行解释器。它已被证明是 Unix 的主要功能之一&#xff0c;并发展成为一个全新的主题。Linux 提供了各种功能强大…

LeetCode 20.有效括号 详解(c语言实现) (⌯꒪꒫꒪)੭

题目详情&#xff1a; 思路&#xff1a;Step1:如果是左括号&#xff0c;入栈 Step2:如果是右括号&#xff0c;就出栈顶的元素与右括号进行比对&#xff0c;如果匹配&#xff0c;继续&#xff0c;直到都匹配成功结束。否则退出&#xff0c;直接返回false. 栗子&#xff1a;1. {…

C#之反编译之路(一)

本文将介绍微软反编译神器dnSpy的使用方法 c#反编译之路(一) dnSpy.exe区分64位和32位,所以32位的程序,就用32位的反编译工具打开,64位的程序,就用64位的反编译工具打开(个人觉得32位的程序偏多,如果不知道是32位还是64位,就先用32位的打开试试) 目前只接触到wpf和winform的桌…

Redis (三)

1、redis复制 简单的概括就是主从复制&#xff0c;master以写为主&#xff0c;Slave以读为主&#xff0c;当master数据发生变化的时候&#xff0c;自动将更新的数据异步同步到其他的slave是数据库。 使用这种机制的话&#xff0c;可以做到读写分离&#xff0c;可以减轻主机负担…

DDoS攻击的多种方式

DDOS攻击指分布式拒绝服务攻击&#xff0c;即处于不同位置的多个攻击者同时向一个或数个目标发动攻击&#xff0c;或者一个攻击者控制了位于不同位置的多台机器并利用这些机器对受害者同时实施攻击。由于攻击的发出点是分布在不同地方的&#xff0c;这类攻击称为分布式拒绝服务…

大数据毕设分享 flink大数据淘宝用户行为数据实时分析与可视化

文章目录 0 前言1、环境准备1.1 flink 下载相关 jar 包1.2 生成 kafka 数据1.3 开发前的三个小 tip 2、flink-sql 客户端编写运行 sql2.1 创建 kafka 数据源表2.2 指标统计&#xff1a;每小时成交量2.2.1 创建 es 结果表&#xff0c; 存放每小时的成交量2.2.2 执行 sql &#x…

提升网络安全重要要素IP地址

在数字化时代&#xff0c;网络安全已经成为人们关注的焦点。本文将深入探讨网络安全与IP地址之间的紧密联系&#xff0c;以及IP地址在构建数字世界的前沿堡垒中的关键作用。 网络安全是当今数字社会中不可忽视的挑战之一。而IP地址&#xff0c;作为互联网通信的基础协议&#…