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

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

相关推荐
Full Stack Developme34 分钟前
数据库索引的原理及类型和应用场景
数据库
IDC02_FEIYA2 小时前
SQL Server 2025数据库安装图文教程(附SQL Server2025数据库下载安装包)
数据库·windows
辞砚技术录2 小时前
MySQL面试题——联合索引
数据库·面试
萧曵 丶3 小时前
MySQL 主键不推荐使用 UUID 的深层原因
数据库·mysql·索引
小北方城市网3 小时前
分布式锁实战指南:从选型到落地,避开 90% 的坑
java·数据库·redis·分布式·python·缓存
毕设十刻3 小时前
基于Vue的人事管理系统67zzz(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
TDengine (老段)5 小时前
TDengine Python 连接器入门指南
大数据·数据库·python·物联网·时序数据库·tdengine·涛思数据
萧曵 丶5 小时前
事务ACID特性详解
数据库·事务·acid
kejiayuan6 小时前
CTE更易懂的SQL风格
数据库·sql
—Miss. Z—6 小时前
Power Query数据分类整合
excel