优化大量数据导出到Excel的内存消耗(二):如果数据超出Excel单表上限,则进行分表

news2024/11/13 9:12:47

优化前:优化大量数据导出到Excel的内存消耗_大文件异步导出 内存占用高-CSDN博客

写Excel文件报错:Invalid row number (1048576) outside allowable range (0..1048575)

写入Excel时遇到`IllegalArgumentException`,原因是超出允许的最大行数。文章提供了解决方案,即使用多个sheet并将数据分批写入以避免此问题。

数据导出优化:如果数据超出Excel单表上限,Excel单表最多可以存储1048576条数据(1024的平方,2的20次方),如果数据超出Excel单表上限,则进行分表。

 public void writeExcel(OutputStream os, String sheetName, Map<String, String> header, List<Map<String, Object>> datas) {
        logger.info("导入数据到excel==========> 开始");
        long startTime = System.currentTimeMillis();  // 记录开始时间
        int rowAccessWindowSize = 100;  // 设置适当的行访问窗口大小
        SXSSFWorkbook wb = new SXSSFWorkbook(rowAccessWindowSize);
        wb.setCompressTempFiles(true);  // 启用临时文件压缩以提高性能

        int sheetIndex = 0; // 新增变量,用于追踪当前的sheet索引
        int maxRowsPerSheet = 1048576; // 单个sheet的最大行数

        int totalRecords = ObjectKit.isNotEmpty(datas) ? datas.size() : 0;  // 总共导出记录数
        logger.info("即将导出记录总数: " + totalRecords);

        Map<String, CellStyle> cellStyles = initStyles(wb); // 优化:样式创建移到循环外部
        while (!datas.isEmpty()) {
            String currentSheetName = sheetName + "_" + sheetIndex;
            Sheet sheet = wb.createSheet(currentSheetName);
            int rowNum = 0;
            Row row = sheet.createRow(rowNum);
//            Map<String, CellStyle> cellStyles = initStyles(wb);
            int cellNum = 0;

            // 写入表头
            for (Map.Entry<String, String> entry : header.entrySet()) {
                String fieldDesc = entry.getValue();
                Cell cell = row.createCell(cellNum);
                cell.setCellValue(fieldDesc);
                logger.info("导入数据到excel==========> 表头" + entry.getKey());
                cellNum++;
            }


            // 计算本次循环需要处理的数据量
            int recordsToProcess = Math.min(maxRowsPerSheet - 1, datas.size()); // 减去1是因为第一行是表头

            for (int i = 0; i < recordsToProcess; i++) {
                Map<String, Object> map = datas.remove(0); // 从列表头部移除已处理的数据
                rowNum++;
                row = sheet.createRow(rowNum);
                cellNum = 0;

                for (Map.Entry<String, String> entry : header.entrySet()) {
                    String fieldName = entry.getKey();
                    Object data = map.get(fieldName.toUpperCase());
                    String dataString = null == data ? "" : data.toString();

                    if (data instanceof BigDecimal) {
                        Cell cell = row.createCell(cellNum);
                        cell.setCellValue(((BigDecimal) data).toPlainString());
                        cell.setCellStyle(cellStyles.get("Number"));
                    } else {
                        if (data instanceof Date || data instanceof Timestamp) {
                            if (data.toString().contains(".")) {
                                dataString = null == data ? "" : data.toString().substring(0, data.toString().indexOf("."));
                            } else {
                                dataString = null == data ? "" : data.toString();
                            }
                        }
                        row.createCell(cellNum).setCellValue(null == data ? "" : dataString);
                    }
                    cellNum++;
                }

                // 当前已导出记录数及进度
                if(rowNum % 10000 == 0){
                    logger.info("当前sheet已导出记录数: " + rowNum + ", 进度: " + ((float) rowNum / recordsToProcess) * 100 + "%");
                }
            }

            sheetIndex++; // 切换到下一个sheet
        }

        logger.info("导入数据到excel==========> 结束");
        long endTime = System.currentTimeMillis();  // 记录结束时间
        long elapsedTime = endTime - startTime;  // 计算耗时时间
        logger.info("总共导出记录数: " + totalRecords);
        long elapsedTimeInSeconds = elapsedTime / 1000;  // 将毫秒转换为秒
        logger.info("耗时时间: " + elapsedTimeInSeconds + " 秒 " );


        try {
            wb.write(os);
        } catch (IOException e) {
            throw new ImpException(ImpError.APP_ERR_20_04_10, e);
        } finally {
            try {
                if (null != wb) {
                    wb.close();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

这一优化措施确保了当数据量超过Excel单表上限时,数据能够被有效地分散到多个工作表中,从而支持更大规模的数据导出。
目前测试了导出三百万数据正常则进行分表。

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

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

相关文章

哪些岗位在拿着年薪百万?

​根据脉脉数据研究院 2020 年 8 月 2 日最新发布的《中国高端人才透视2020》(以下简称“报告”)&#xff0c;高端人才占比的最多的行业&#xff0c;其实不是互联网。 报告显示&#xff0c;以高薪金领人才相比于该行业的整体人才数量的占比来计算&#xff0c;排名前三的是金融、…

【分割大模型】SAM2(Segment Anything2)新的分割一切大模型(原理+安装+代码)

文章目录 前言1.特点2.结构3.应用场景 一、原理1.1 引言1.2 任务&#xff1a;可提示的视觉分割1.3 模型1.4 数据引擎 与 SA-V数据集 二、安装与使用 项目地址&#xff1a;https://github.com/facebookresearch/segment-anything-2 前言 Segment Anything Model 2&#xff08;S…

使用SSL认证访问操作手册

完整版 【金山文档 | WPS云文档】 使用SSL认证访问操作手册 https://kdocs.cn/l/cuxGfHD17eEw

vue2.0和vue3.0区别

vue2.0和vue3.0区别 双向数据绑定的原理改变&#xff1a;‌ Vue2使用Object.defineProperty对数据进行劫持&#xff0c;‌结合发布订阅模式实现双向数据绑定&#xff0c;‌而Vue3则采用了ES6的Proxy API对数据进行代理&#xff0c;‌提供了更多的拦截操作&#xff0c;‌能够监…

KBL406-ASEMI、AI智能专用整流桥KBL406

编辑&#xff1a;ll KBL406-ASEMI、AI智能专用整流桥KBL406 型号&#xff1a;KBL406 品牌&#xff1a;ASEMI 封装&#xff1a;KBL-4 批号&#xff1a;2024 现货&#xff1a;50000 正向电流&#xff08;Id&#xff09;&#xff1a;4A 反向耐压&#xff08;VRRM&#xff…

【智能流体力学】ANSYS Fluent流体仿真基础深度学习驱动的前期准备:CAX计算机辅助集成技术

目录 一、CAX计算机辅助集成技术二、计算机辅助工程(CAE)三、SCDM (Species Concentration Display Model) 显示和分析物质浓度分布的模型1. **SCDM概述**2. **主要功能**3. **功能特点**4. **使用步骤**5. **应用实例**6. **优点与限制**四、行业应用五、Fluent 软件功能1. …

net.sf.jsqlparser.statement.select.SelectItem

今天一启动项目&#xff0c;出现了这个错误&#xff0c;仔细想了想&#xff0c;应该是昨天合并代码&#xff0c;导致的mybatis-plus版本冲突&#xff0c;以及分页PageHelper版本不兼容 可以看见这个我是最下边的 Caused by 报错信息&#xff0c;这个地方提示我 net .s…

Java面试题--JVM大厂篇之实战解析:如何通过CMS GC优化大规模Java应用的响应时间

引言&#xff1a; 下午好&#xff0c;各位Java开发者&#xff01;在实际项目中&#xff0c;性能优化一直是我们关注的重点&#xff0c;特别是在面对大规模Java应用时&#xff0c;响应时间的优化更是至关重要。今天&#xff0c;我们将通过实战案例&#xff0c;深入解析如何利用C…

【JAVA多线程】JDK线程同步工具类:Semaphore、CountDownLatch、CyclicBarrier

目录 1.可能会遇到的线程协作场景 2.Semaphore 3.CountDownLatch 4.CyclicBarrier 1.可能会遇到的线程协作场景 在并发编程中&#xff0c;线程除了独自向前运行&#xff0c;还可能相互之间要进行协作&#xff0c;以保证完成最终总的目标。可能会遇到的几种任务之间的协作&…

『 C++ 』单例模式与IO流

文章目录 单例模式饿汉加载的单例模式实现懒汉加载的单例模式实现 IO流类型之间的转换 单例模式 单例模式是一种创建型设计模式; 确保一个类在应用程序的生命周期内仅有一个实例并提供一个全局访问点来访问该实例; 单例模式主要目的是为了控制某些类的实例化以避免产生多个实例…

Java面试--框架--Spring MVC

Spring MVC 目录 Spring MVC1.spring mvc简介2.spring mvc实现原理2.1核心组件2.2工作流程 3.RESTful 风格4.拦截器4.1过滤器、监听器、拦截器的对比4.2 拦截器基本概念4.3 拦截器的实现 1.spring mvc简介 Spring MVC是一款由Spring Framework 提供的 web组件&#xff0c;实现…

MySQL-MVCC举例说明

在数据库系统中&#xff0c;多版本并发控制&#xff08;MVCC, Multi-Version Concurrency Control&#xff09; 是一种用于提高并发性能的机制&#xff0c;它允许多个事务同时读取和写入数据&#xff0c;而不会产生锁等待和阻塞的问题。MySQL 的 InnoDB 存储引擎广泛使用了 MVC…

C#MVC返回DataTable到前端展示。

很久没写博客了&#xff0c;闭关太久&#xff0c;失踪人口回归&#xff0c;给诸位道友整点绝活。 交代下背景&#xff1a;要做一个行转列的汇总统计&#xff0c;而且&#xff0c;由于是行转列&#xff0c;列的数量不固定&#xff0c;所以&#xff0c;没法使用正常的SqlSugar框…

C++入门基础知识13

C 的关键字&#xff08;接上一篇博文&#xff09;&#xff01;&#xff01; 10. const_cast用法&#xff1a; 该运算符用来修改类型的 const 或 volatile 属性。除了 const 或 volatile 修饰之外&#xff0c; type_id 和 expression 的类型是一样的。常量指针被转化成非常量指针…

催收业务怎么提高接通率

提高催收呼叫业务的接通率是一个综合性的任务&#xff0c;需要从多个方面进行优化。以下是一些具体的策略和建议&#xff1a; 一、优化呼叫时间与频次 1. 选择合适的呼叫时间&#xff1a;通过分析目标客户的活跃时段&#xff0c;选择他们最可能接听电话的时间进行呼叫…

iOS Object-C 创建类别(Category) 与使用

有时候使用系统给出类或者第三方的类,但是呢它们自带的属性和方法又太少,不够我们的业务使用,这时候就需要给“系统的类或者第三方类”创建一个类别(Category),把自己的想添加的属性和方法写进来. Category模式用于向已经存在的类添加方法从而达到扩展已有类的目的 一:创建Ca…

零碳工厂:我国工业转型升级的绿色引擎

面对全球气候变化的严峻挑战&#xff0c;我国提出了碳达峰和碳中和的宏伟目标。零碳工厂作为工业领域实现这一目标的重要途径&#xff0c;正成为推动我国工业转型升级的绿色引擎。本文将提供一站式零碳工厂服务指南&#xff0c;帮助企业迈向零碳排放&#xff0c;共同构建绿色低…

零售企业做好人事管理并不难!智能化人事管理平台解决企业三大痛点!

在零售行业的激烈竞争中&#xff0c;优秀的人事管理策略是企业成功的关键。然而&#xff0c;传统的人事管理模式常常面临人员分散、流程繁琐、跨部门协作困难等挑战。为了应对这些问题&#xff0c;一站式人事管理平台的出现&#xff0c;为企业提供了数字化的解决方案。本文将探…

[论文泛读]zkLLM: Zero Knowledge Proofs for Large Language models

文章目录 介绍实验数据实验数据1实验数据2实验数据3 介绍 这篇文章发在CCS2024&#xff0c;CCS是密码学领域的顶会。作者是来自加拿大的University of Waterloo。文章对大语言模型像GPT和LLM等大语言模型实现了零知识可验证执行&#xff0c;但不涉及零知识可验证训练。个人觉得…

PAT--1124.最近的斐波那契数

题目描述 算法分析 找到把n夹在中间的两个斐波那契数列的数字&#xff0c;比如输入的数字9&#xff0c;你需要找到数字8和13&#xff0c;然后再输出差值绝对值较小的那个数字&#xff0c;如果一样&#xff0c;输出较小的数字 完整代码 #include<iostream> using names…