Jersey服务文件上载导致OutOfMemoryError

gki*_*iko 14 file-upload multipartform-data heap-memory jersey

我正在使用Jersey 2.0开发表单提交服务.表单包括几个文本字段和一个文件字段.我需要提取文件,文件名,文件媒体类型文件内容类型,并将它们保存在对象库中.

@Path("upload")
@Consumes({MediaType.MULTIPART_FORM_DATA})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class UploadService {
    @POST
    public BlobDo uploadFile(FormDataMultiPart uploadedBody) {
        String accountSid = uploadedBody.getField("account-sid").getValue();
        String apiToken = uploadedBody.getField("api-token").getValue();
        String checksum = uploadedBody.getField("checksum").getValue();

        FormDataBodyPart bodyPart = uploadedBody.getField("file");
        MySwiftObject obj = new MySwiftObject(bodyPart.getValueAs(InputStream.class));
        obj.setName(bodyPart.getContentDisposition().getFileName());
        obj.setContentType(bodyPart.getMediaType().toString());
        obj.setContentDisposition(bodyPart.getContentDisposition().toString());
   ...
}
Run Code Online (Sandbox Code Playgroud)

的pom.xml

<jersey.version>2.17</jersey.version>

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet-core</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-inmemory</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-server</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-bean-validation</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

表单提交请求

POST /nbs/v2/upload HTTP/1.1
Host: 127.0.0.1:8080
Cache-Control: no-cache
Postman-Token: a4c1d4e9-5f71-2321-3870-e9cac0524f8d
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryA2Z9pPMA7y3da8BG

------WebKitFormBoundaryA2Z9pPMA7y3da8BG
Content-Disposition: form-data; name="account-sid"

Q45Ppm5ukvdqjTQ6eW0O5ztTXipwnjKQx1p6cf+fbCQ=
------WebKitFormBoundaryA2Z9pPMA7y3da8BG
Content-Disposition: form-data; name="api-token"

6397cd691909fdc14cef67dbc1dc2dc3
------WebKitFormBoundaryA2Z9pPMA7y3da8BG
Content-Disposition: form-data; name="file"; filename="screen_4_100155.jpg"
Content-Type: image/jpeg

......Exif..MM.*.............................b...........j
------WebKitFormBoundaryA2Z9pPMA7y3da8BG
Content-Disposition: form-data; name="checksum"

6a3381b1d16bded4a3dfc325a8bb800e
------WebKitFormBoundaryA2Z9pPMA7y3da8BG
Run Code Online (Sandbox Code Playgroud)

JVM堆大小

-Xmx=1024mb
Run Code Online (Sandbox Code Playgroud)

问题

上传~50MB文件时,会在/tmp/tomcat7-tomcat7-tmp名称FileBackedOutputStream7949386530699987086.tmp和目录中创建两个具有相似MD5总和的临时文件MIME8234229766850016150.tmp

在上传完成之前,服务器抛出异常

javax.servlet.ServletException: org.glassfish.jersey.server.ContainerException: java.lang.OutOfMemoryError: Java heap space
    org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:421)
    org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:386)
    org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:335)
    org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:222)

并删除具有名称的文件MIME8234229766850016150.tmp但保留另一个文件.未删除的FileBackedOutputStream....tmp文件填满了硬盘上的整个空间.


我做了什么

  1. 堆空间增加到7GB,但无法上传~200MB文件.

  2. 在服务器上运行作业以删除旧的临时文件.

  3. 创建了包含名称jersey-multipart-config.properties和内容的文件

    jersey.config.multipart.bufferThreshold = -1

除非重新启动tomcat,否则MIME[random numbers].tmp不再创建该文件,但FileBackedOutputStream[random number].tmp仍会挂起在硬盘驱动器上.


  1. 如果不在我的硬盘上留下临时文件,Jersey如何处理大文件(可能是1GB)?最好的情况是根本不使用硬盘驱动器并通过内存传输小块.

  2. 如果输入流由文件支持,为什么会出现堆溢出


我读过的材料

  • 到目前为止我发现的结束解释.
  • 这家伙有类似的问题,但在客户端.
  • 可能包含解决方案,但无法理解答案.
  • 非常接近我的问题,但无法解决.
  • bufferThreshold的想法来自这里.

gki*_*iko 2

看来问题 #1 是通过在我的web.xml<servlet>标签下添加以下行来解决的

<multipart-config>
        <location>/tmp</location>
        <max-file-size>1000000000</max-file-size>
        <max-request-size>1500000000</max-request-size>
        <file-size-threshold>0</file-size-threshold>
</multipart-config>
Run Code Online (Sandbox Code Playgroud)

并删除jersey-multipart-config.properties文件。

现在我可以上传超过 200Mb 的文件。不再创建临时文件。

但我仍然无法解释问题#2。