我的问题很简单。我想验证最大 50MB 的文件以确保 App Engine 中的格式正确。
现在这带来了许多巨大的挑战。第一个是 Apache XLS/XLSX POI API。当我在验证之前将 20MB 的文件数据本地加载到内存中时,它会抛出:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2271)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:113)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:140)
at org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource$FakeZipEntry.<init>(ZipInputStreamZipEntrySource.java:128)
at org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource.<init>(ZipInputStreamZipEntrySource.java:55)
at org.apache.poi.openxml4j.opc.ZipPackage.<init>(ZipPackage.java:84)
at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:272)
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:79)
我需要打开和验证大小不超过 20 到 25MB 的电子表格。如果可能的话,50 将是一个不错的延伸目标。我们在一张纸上谈论数十万行数据。
现在,我的传统代码将整个文件加载到内存中,并立即着手使我的应用引擎实例的堆崩溃。这是我的传统代码:
public ErrorLog validateWorkbook(inputWorkbook)
{
int sheetCount = inputWorkbook.getNumberOfSheets();
for (int x = 0; x< sheetCount; x++)
{
Sheet currentSheet = inputWorkbook.getSheetAt(x);
Iterator<Row> rowIterator = currentSheet.rowIterator();
while(rowIterator.hasNext())
{
Iterator<Cell> cellIterator = rowIterator.next().cellIterator();
while(cellIterator.hasNext())
{
Cell currentCell = cellIterator.next();
boolean success = validateCellContents(currentCell);
if(!success)
ErrorLog.appendError(new Error()); // detailed user error explicitly defining error location, cell value, and recommended steps to fix
}
}
}
return ErrorLog;
}
现在有一种基于事件的方法可以在每次遇到单元格时处理一个 Action 监听器。但是虚拟代码 here引用了:
ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(container);
我已经在调试器中检查了这个对象,它包含当前工作表中的每个唯一字符串引用。这本质上正是在做我想避免的事情。它分配一大块内存来预先将每个值存储在内存中。理想的解决方案采用输入字节流并在遍历文件时解码字符串以减少内存占用。
因为字符串表肯定会占用大量的内存空间。我正在处理 150,000 到 300,000 个订单项电子表格
现在 quick guide提到您可以使用 File 或 InputStream 并且如果您使用 File,输入将被缓冲。这里的问题是 App Engine 和 Blob Store Service 不了解文件对象并且只返回 InputStreams(据我所知)。
此外,另一个事件驱动模型,Default Handler ,在其接口(interface)定义的方法中似乎没有任何关于每个值的列或行的概念,这些方法在操作发生时被调用(并且它还预先分配整个共享字符串表)。
这里的想法用完了!打算为此提供赏金。至少一个具体的“不,这是不可能的”就足够了,然后我可以开始寻找解决方法,但我觉得我只是没有尽可能好地使用大量 API。
最佳答案
提倡,
你可以做到这一点, 但您必须发挥创意才能绕过 GAE 的一些限制。
首先,App Engine 前端实例有 1 分钟的请求限制,因此如果您想处理 50 MB 的文件,您将不得不使用 task queues 之一。或使用“Manual/basic scaling module”来逃避时间限制。
其次,内存。同样,您有 2 个选择,Using modules您可以更好地控制您的实例内存,这是朝着正确方向迈出的一步,但它不会很好地扩展。
我遇到过你的情况,最后我使用了 Google Drive API + Google Spreadheets API或 Blobstore service取决于要求。使用这些备选方案中的任何一个,我上传了 excel 文件,这样我就可以使用队列离线批量处理它们。
关于java - Apache POI WorkbookFactory.create 抛出 java.lang.OutOfMemoryError : Java heap space,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25796777/