You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Jxls-core 1.0.2导出大量数据遇内存问题求助

解决Jxls-core 1.0.2大数据量导出OOM问题:分块写入方案

嘿,这个问题太常见了!Jxls 1.x版本由于是一次性将所有数据加载到内存中进行模板渲染,处理4万+数据时确实容易触发内存溢出,而你提到的5000条刚好可以作为完美的分块阈值,分块写入完全能解决这个问题,我给你两种可行的实现思路:

方案一:分Sheet分批写入(简单易实现)

这种方式是把大数据集拆分成多个5000条的批次,每个批次生成一个独立的Sheet,避免单Sheet数据量过大导致内存压力。

实现步骤

  1. 准备一个包含用户数据模板的单Sheet Excel(比如表头+一条数据的模板,或者直接用你原来的模板)
  2. 循环拆分用户列表,每次取5000条作为当前批次
  3. 复制模板Sheet并填充当前批次的数据
  4. 最后统一写入输出流

代码示例

// 读取模板文件
InputStream templateStream = new FileInputStream("user_template.xlsx");
Workbook workbook = WorkbookFactory.create(templateStream);
templateStream.close();

int batchSize = 5000;
int totalUsers = users.size();
int sheetCounter = 1;

// 复用XLSTransformer,避免重复创建对象
XLSTransformer transformer = new XLSTransformer();

for (int i = 0; i < totalUsers; i += batchSize) {
    int endIndex = Math.min(i + batchSize, totalUsers);
    List<User> currentBatch = users.subList(i, endIndex);
    
    // 复制模板Sheet作为新的批次Sheet
    Sheet templateSheet = workbook.getSheetAt(0);
    Sheet newBatchSheet = workbook.cloneSheet(0);
    // 重命名Sheet,方便区分批次
    workbook.setSheetName(workbook.getSheetIndex(newBatchSheet), "用户列表_" + sheetCounter);
    
    // 准备当前批次的上下文数据
    Map<String, Object> beans = new HashMap<>();
    beans.put("users", currentBatch);
    
    // 填充当前Sheet的数据
    transformer.transformWorkbook(workbook, beans, newBatchSheet.getSheetName());
    
    sheetCounter++;
    // 手动置空当前批次列表,帮助GC回收内存
    currentBatch = null;
}

// 写入最终Excel文件
try (FileOutputStream out = new FileOutputStream("large_user_export.xlsx")) {
    workbook.write(out);
} catch (IOException e) {
    e.printStackTrace();
}

方案二:同Sheet分批追加写入(保持单Sheet结构)

如果你希望所有数据都在同一个Sheet里,可以通过控制Jxls的渲染区域起始行,实现分批追加数据,这种方式需要你的模板定义了正确的重复渲染区域(比如用jx:each标签循环数据)。

实现步骤

  1. 准备包含jx:each循环标签的模板,确保模板的重复区域可以被复用
  2. 拆分用户列表为5000条的批次
  3. 每次渲染时调整区域的起始行,将当前批次数据追加到上一批次的下方
  4. 最后统一写入输出流

代码示例

InputStream templateStream = new FileInputStream("user_template_with_loop.xlsx");
Workbook workbook = WorkbookFactory.create(templateStream);
templateStream.close();

Sheet targetSheet = workbook.getSheetAt(0);
int batchSize = 5000;
int totalUsers = users.size();
// 假设表头在第1行,数据从第2行开始渲染
int currentStartRow = 2;

// 构建模板中的数据渲染区域(根据你的模板实际列调整,比如A到E列)
AreaBuilder areaBuilder = new XlsAreaBuilder(workbook, new SimpleAreaListener());
// 模板中定义的循环区域(这里假设模板里预留了基础循环行,Jxls会自动扩展)
Area dataArea = areaBuilder.buildArea("Template!A2:E2");

for (int i = 0; i < totalUsers; i += batchSize) {
    int endIndex = Math.min(i + batchSize, totalUsers);
    List<User> currentBatch = users.subList(i, endIndex);
    
    Map<String, Object> context = new HashMap<>();
    context.put("users", currentBatch);
    
    // 设置当前批次的渲染起始行,实现追加
    dataArea.setStartRow(currentStartRow);
    dataArea.applyAt(new CellRef("Template!A" + currentStartRow), context);
    
    // 更新下一批次的起始行
    currentStartRow += currentBatch.size();
    // 帮助GC回收
    currentBatch = null;
}

// 写入最终文件
try (FileOutputStream out = new FileOutputStream("single_sheet_large_export.xlsx")) {
    workbook.write(out);
} catch (IOException e) {
    e.printStackTrace();
}

额外优化建议

  • 复用核心对象:像XLSTransformer这类对象不要在循环内重复创建,减少对象创建开销
  • 调整JVM参数:可以配合设置-Xmx2G(根据服务器配置调整)来增加堆内存,作为分块方案的辅助优化
  • 清理无用对象:循环结束后手动置空批次数据列表,帮助JVM及时回收内存
  • 简化模板:模板中尽量避免复杂公式、图片等资源,减少内存占用

内容的提问来源于stack exchange,提问作者user3191018

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:15:53