Подтвердить что ты не робот

При загрузке файла службы Джерси вызывается OutOfMemoryError

Я разрабатываю службу подачи формы с помощью Джерси 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());
   ...
}

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>

запрос на отправку формы

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

Размер кучи JVM

-Xmx=1024mb

Проблема

При загрузке файла ~ 50 МБ в каталоге /tmp/tomcat7-tomcat7-tmp создаются два временных файла с похожими суммами MD5 с именем FileBackedOutputStream7949386530699987086.tmp и 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, но оставляет его. И undeleted FileBackedOutputStream....tmp файлы заполняют все пространство на жестком диске.


Что я сделал

  • Увеличьте кучу пространства до 7 ГБ, но файлы ~ 200 МБ не могут быть загружены.

  • Запуск задания на сервере для удаления старых временных файлов.

  • Созданный файл с именем jersey-multipart-config.properties и контентом

    jersey.config.multipart.bufferThreshold = -1

Файл MIME[random numbers].tmp больше не создается, но FileBackedOutputStream[random number].tmp по-прежнему зависает на жестком диске, если не перезагружен tomcat.


Вопрос

  • Как Джерси может обрабатывать большие файлы (возможно, 1 ГБ), не оставляя временные файлы на моем жестком диске? Лучшим случаем было бы не использовать жесткий диск вообще и передавать небольшие куски через память.

  • Почему я получаю переполнение кучи, если входной поток поддерживается файлами?


Материалы, которые я прочитал

  • Закрывает объяснение, которое я нашел до сих пор. читать
  • У этого парня есть аналогичная проблема, но на стороне клиента. читать
  • Может содержать решение, но не может понять ответ. читать
  • Довольно близко к моей проблеме, но не могу ее решить. читать
  • bufferThreshold идея была взята отсюда. читать
4b9b3361

Ответ 1

Кажется, что проблема № 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>

и удалил файл jersey-multipart-config.properties.

Теперь я могу загружать файлы размером более 200 Мб. Создание временных файлов не производится.

Но я все еще не могу объяснить проблему №2.