在加载一个带有大量“带状行样式”(banded rows)的Excel文件时,调用 Workbook 构造方法时触发 java.lang.OutOfMemoryError ,导致程序崩溃。具体位置为 Workbook wbk = new Workbook(formatFile.getLocalFile()); 。
JDK:17
aspose-cells:21.11 已试过26.4版本 该问题仍存在
JVM堆内存设置:-Xms32g -Xmx32g
相关代码片段:
public CellsFormatAdapter(FormatFile formatFile) throws Exception {
this.formatFile = formatFile;
Workbook wbk = new Workbook(formatFile.getLocalFile());
// 执行函数计算
calculateFormula(wbk);
// 检查 Sheet 页中是否存在打印区域设置
checkAndClearPrintArea(wbk.getWorksheets());
}
Excel内容示例如下图:
image.png (22.0 KB)
使用新版本异常信息如下图:
image.png (80.9 KB)
您好,
我们已经收到了您关于加载包含大量带状行样式的 Excel 文件时出现 java.lang.OutOfMemoryError 的反馈。
我们尝试根据您提供的代码片段和截图进行复现,但在常规测试中未能重现该 OOM 异常。由于 32GB 的堆内存(-Xmx32g)对于大多数 Excel 处理场景应当是充足的,该问题可能与特定文件内部的样式堆积或结构损坏有关。
为了进一步定位问题原因,请提供以下信息:
- 能够触发该异常的 Excel 源文件。仅凭截图我们无法分析文件的内部 XML 结构。
- 如果该文件包含敏感数据,您可以将其删除,只要确保处理后的文件仍能复现该 OOM 问题即可。
收到您的样本文件后,我们将立即进行深入测试。
您好,
异常的文件如下:
error file.7z (830.4 KB)
@Whataya_Want_From_Me
您好,
感谢您提供用于复现问题的示例文件。
我们已成功接收到该压缩包,并将其关联至正在进行的调查流程中。我们的开发团队将使用此文件对解析带状行样式(banded rows)时的内存占用情况进行深入分析,以定位导致 OutOfMemoryError 的具体原因。
一旦有任何进展或修复方案,我们会第一时间在此贴中通知您。感谢您的耐心配合。
@Whataya_Want_From_Me ,
您的源文件“error file.xlsx”用Aspose.Cells for Java 26.4/26.5可以成功加载,仅占用几十MB的内存。
请提供一段可运行的代码,以便我们重现您遇到的问题。
public static void test() throws Exception
{
System.out.println(CellsHelper.getVersion());
// =====================================================
// Load workbook
// =====================================================
long memBeforeLoad = usedMemory();
long startLoad = System.nanoTime();
Workbook wb = new Workbook("error file.xlsx");
long endLoad = System.nanoTime();
long memAfterLoad = usedMemory();
printStats(
"Workbook Load",
startLoad,
endLoad,
memBeforeLoad,
memAfterLoad);
gcPause();
// =====================================================
// Calculate formulas
// =====================================================
long memBeforeCalc = usedMemory();
long startCalc = System.nanoTime();
wb.calculateFormula();
long endCalc = System.nanoTime();
long memAfterCalc = usedMemory();
printStats(
"calculateFormula()",
startCalc,
endCalc,
memBeforeCalc,
memAfterCalc);
wb.dispose();
}
private static long usedMemory() {
Runtime rt = Runtime.getRuntime();
return rt.totalMemory() - rt.freeMemory();
}
private static void gcPause() throws Exception {
System.gc();
Thread.sleep(1000);
}
private static void printStats(
String title,
long startTime,
long endTime,
long memBefore,
long memAfter) {
double timeMs = (endTime - startTime) / 1_000_000.0;
long memDiff = memAfter - memBefore;
System.out.println("========== " + title + " ==========");
System.out.printf("Time Cost : %.2f ms%n", timeMs);
System.out.printf("Memory Before : %.2f MB%n",
memBefore / 1024.0 / 1024.0);
System.out.printf("Memory After : %.2f MB%n",
memAfter / 1024.0 / 1024.0);
System.out.printf("Memory Delta : %.2f MB%n",
memDiff / 1024.0 / 1024.0);
System.out.println();
}
测试类代码如下:
JobExecutorTest.7z (1.4 KB)
@Whataya_Want_From_Me ,
在Excel打印预览中,工作表“底数据”有超过12万页。你的代码设置了OnePagePerSheet,一个工作表的所有内容都会输出到的pdf的一页中。把12万页输出到一个pdf页是几乎不可能的,导致了OOM异常。
@peyton.xu
但是有数据的行数仅有9000多行,其余行数都是带状行样式,并未有数据,如何仅读取有数据的部分呢?
@Whataya_Want_From_Me ,
工作表“底数据”,如果只要打印有数据的部分,一般情况下,都是在工作表上设置合适的打印区域。
不过你的代码有清理打印区域的逻辑,所以只能修改源文件了,删除掉没有数据的行。
另外,一个工作表如果有超过几百/上千的页面,是不建议应用OnePagePerSheet选项的。
@Whataya_Want_From_Me
工作表“底数据”中添加了一个整列的Table。Excel会打印所有有数值或者样式的单元格,Aspose.Cells 现在和Excel行为一致。 如果你不希望打印这些多余的行,你可以如下操作:
1,不要设置整列的Table, 重新设置Table只包含A1:AE9157
2, 设置打印区域,可以在Excel中设置A1:AE9157为打印区域
也可以使用代码:
public static void main(String[] args) throws Exception {
// 1. 加载许可证(如果有),无许可证会有水印
// License license = new License();
// license.setLicense("Aspose.Cells.lic");
// 2. 加载 Excel 文件
Workbook workbook = new Workbook("test.xlsx");
Worksheet worksheet = workbook.getWorksheets().get("底数据");
// 3. 获取工作表中**有数据的最大行、最大列**(关键)
Cells cells = worksheet.getCells();
int maxDataRow = cells.getMaxDataRow(); // 最大数据行索引(从0开始)
int maxDataColumn = cells.getMaxDataColumn(); // 最大数据列索引(从0开始)
// 4. 自动设置打印区域 = 从 A1 到 最大数据单元格
// 格式:$A$1:$最后列$最后行
String printArea = "$A$1:$" + CellsHelper.cellIndexToName(maxDataRow, maxDataColumn);
worksheet.getPageSetup().setPrintArea(printArea);
System.out.println("已设置打印区域:" + printArea);
// 5. 保存文件
workbook.save("output_with_print_area.pdf");
System.out.println("文件已保存!");
}
@simon.zhao
您提供的代码中,在我这边编译报错,Cells.cellIndexToName()没有这个方法,您这边用的com.aspose.cells是哪个版本?
Cells.cellIndexToName(0, maxDataColumn));
@Whataya_Want_From_Me
您好,
关于您提到的 Cells.cellIndexToName() 编译错误问题,这是因为在 Aspose.Cells for Java 中,该方法属于静态工具方法。
请确保您使用的是 CellsHelper.cellIndexToName(int row, int column) 而不是直接通过 Cells 实例调用。正确的调用方式如下:
// 正确的 API 调用方式
String cellName = CellsHelper.cellIndexToName(row, column);
该方法在您目前尝试的 21.11 及 24.6 等版本中均是稳定可用的。关于您反馈的 OOM 异常,我们的开发团队正在结合相关逻辑进行内部排查,一旦有进一步的调查结果或优化进展,我们会立即在此贴中告知您。