优化前:优化大量数据导出到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单表上限时,数据能够被有效地分散到多个工作表中,从而支持更大规模的数据导出。
目前测试了导出三百万数据正常则进行分表。