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

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

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

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

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

java 复制代码
 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单表上限时,数据能够被有效地分散到多个工作表中,从而支持更大规模的数据导出。

目前测试了导出三百万数据正常则进行分表。

相关推荐
Karoku06620 分钟前
【企业级分布式系统】ELK优化
运维·服务器·数据库·elk·elasticsearch
小技与小术2 小时前
数据库表设计范式
数据库·mysql
安迁岚2 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验三 数据操作
运维·服务器·数据库·sql·mysql
安迁岚2 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验九 触发器
数据库·sql·mysql·oracle·实验报告
Loganer2 小时前
MongoDB分片集群搭建
数据库·mongodb
LKID体2 小时前
Python操作neo4j库py2neo使用之创建和查询(二)
数据库·python·neo4j
刘大浪2 小时前
后端数据增删改查基于Springboot+mybatis mysql 时间根据当时时间自动填充,数据库连接查询不一致,mysql数据库连接不好用
数据库·spring boot·mybatis
一只爱撸猫的程序猿2 小时前
简单实现一个系统升级过程中的数据平滑迁移的场景实例
数据库·spring boot·程序员
无敌岩雀2 小时前
MySQL中的索引
数据库·mysql
a_安徒生3 小时前
linux安装TDengine
linux·数据库·tdengine