上传到 Blobstore 会产生 Java 堆 OutOfMemoryError

Posted

技术标签:

【中文标题】上传到 Blobstore 会产生 Java 堆 OutOfMemoryError【英文标题】:Uploading to Blobstore gives a Java heap OutOfMemoryError 【发布时间】:2011-03-11 18:19:37 【问题描述】:

我正在为我的 Google App Engine 应用程序制作一个非常简单的上传表单。在客户端 GWT 代码中,我有类似的内容:

final FormPanel uploadForm = new FormPanel();
uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);
uploadForm.setMethod(FormPanel.METHOD_POST);

uploadBtn.addClickHandler(new ClickHandler() 
        @Override
        public void onClick(ClickEvent event) 
            blobstoreUploadURLService.getBlobstoreUploadURL("/banzai/process-pdf", new AsyncCallback<String>() 

                @Override
                public void onFailure(Throwable caught) 
                    // TODO Auto-generated method stub
                    System.err.println("FAILURE DURING UPLOAD SERVICE");
                

                @Override
                public void onSuccess(String result) 
                    uploadForm.setAction(result);
                    uploadForm.submit();
                

            );
        
    );

它使用new FileUpload() 来选择文件。当我在本地或已部署的实例上对其进行测试时,我在日志中收到以下错误:

WARNING: Error for /_ah/upload/agdrYnNrYWFychsLEhVfX0Jsb2JVcGxvYWRTZXNzaW9uX18YAww java.lang.OutOfMemoryError: Java heap space
  at java.util.Arrays.copyOf(Arrays.java:2786)
  at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:71)
  at javax.mail.internet.MimeMultipart.readTillFirstBoundary(MimeMultipart.java:316)
  at javax.mail.internet.MimeMultipart.parse(MimeMultipart.java:186)
  at javax.mail.internet.MimeMultipart.getCount(MimeMultipart.java:109)
  at com.google.appengine.api.blobstore.dev.UploadBlobServlet.handleUpload(UploadBlobServlet.java:135)
  at com.google.appengine.api.blobstore.dev.UploadBlobServlet.access$000(UploadBlobServlet.java:72)
  at com.google.appengine.api.blobstore.dev.UploadBlobServlet$1.run(UploadBlobServlet.java:100)
  at java.security.AccessController.doPrivileged(Native Method)
  at com.google.appengine.api.blobstore.dev.UploadBlobServlet.doPost(UploadBlobServlet.java:98)
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:713)
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
  at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
  at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:51)
  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
  at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
  at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:122)
  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
  at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
  at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
  at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
  at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
  at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
  at com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:70)
  at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
  at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:349)
  at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
  at org.mortbay.jetty.Server.handle(Server.java:326)
  at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
  at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:938)

有趣的是,以前似乎有几个人遇到过这个问题(只需查看 this issue here,您可以在 Google 上找到更多)但似乎没有人知道为什么会发生这种情况,有些人甚至暗示这是 App Engine 的错。在我得出如此轻率的结论之前,我想我先在这里问一下:)

如果有趣的话,这里是尝试上传的 HTTP 标头(一个非常小的文件),使用 HTTP Live Headers 捕获。

POST /_ah/upload/AMmfu6ZyyhSgz9uOR5VX4QBZeYADTB-aSejVvfGaogl3E_E8yPOLgtX9-0mob17IYfsaRZg-YP7aZrp1D4pDAwuKKm9CoNjeVx1eN2PwBro9x0PqXPeBLpQ/ALBNUaYAAAAATDFOaLPIvuEEhSS6F 主持人:kbskaar.appspot.com 用户代理:Mozilla/5.0(Macintosh;U;Intel Mac OS X 10.6;en-US;rv:1.9.2.6)Gecko/20100625 Firefox/3.6.6 接受:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 接受语言:en-us,en;q=0.5 接受编码:gzip,deflate 接受字符集:ISO-8859-1,utf-8;q=0.7,*;q=0.7 保活:115 连接:保持活动 推荐人:http://kbskaar.appspot.com/ 饼干:姓=黄;名字=Ka%20Man%20Sophia;用户名=kmswong%40uwaterloo.ca 内容类型:multipart/form-data;边界=---------------168072824752491622650073 内容长度:57 --------------------------168072824752491622650073-- HTTP/1.1 500 内部服务器错误 服务器:上传服务器建于 2010 年 7 月 1 日 15:26:59 (1278023219) 内容类型:文本/html;字符集=UTF-8 X-AppEngine-Estimated-CPM-US-Dollars:0.375815 USD X-AppEngine-Resource-Usage: ms=7103 cpu_ms=16217 api_cpu_ms=0 日期:2010 年 7 月 5 日星期一 03:06:00 GMT Pragma:无缓存 过期:格林威治标准时间 1990 年 1 月 1 日星期五 00:00:00 缓存控制:无缓存、无存储、必须重新验证 内容长度:3211 -------------------------------------------------- --------

如果您有任何想法,请告诉我。谢谢!

【问题讨论】:

您上传的文件有多大? @markovuksanovic 任何大小的文件都会发生这种情况,甚至是小于 10KB 的文件。绝对不是我上传太多的情况——当然,这也是我首先想到的:) 另外,您可以在上面的代码清单中看到文件的全部内容。这是 57 个字节。 【参考方案1】:

并确保将其添加到您的表单中

enctype="multipart/form-data"

【讨论】:

【参考方案2】:

好的,结果是我在表单的FileInput 元素上遗漏了一个“名称”属性,这真是令人难以置信的愚蠢问题。这显然会使生成的流无法解析,从而导致 MimeMultipart 解析器内存不足。

即使问题最终是我的问题,并且很容易修复,我仍然认为这是 AppEngine 中的一个错误,因为这样一个简单的错误不会导致 OutOfMemoryError 和崩溃;这也可能是仅通过制作恶意 HTTP 请求的 DOS 漏洞的来源。我会向 Google 提交错误报告。

【讨论】:

我遇到了同样的问题,但在传递给 createUploadUrl() 的字符串上也没有前导“/”。这会导致空指针异常 - 请参阅 code.google.com/p/googleappengine/issues/detail?id=2771 如果在上传之前禁用 FileUpload 组件(以防止用户更改值)这也会导致内存不足异常。

以上是关于上传到 Blobstore 会产生 Java 堆 OutOfMemoryError的主要内容,如果未能解决你的问题,请参考以下文章

如何从不具有BlobStore的Flask表单直接将视频文件上传到Cloud Storage?

从 android 客户端将图像存储到 Blobstore 并检索 blobkey 并上传 url 以存储在 Datastore 中。 - 盖伊

如何在将 Blob 上传到 Blobstore 的 POST 请求中提交文本字段并在 Blob 的上传处理程序中检索它?

如何在 BlobStore 中设置文件名属性?

GAE Blobstore:ImagesService.getServingUrl() 给了我更小的图像

python 使用google appengine blobstore api上传的Flask示例。