我正在使用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());
...
}
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
问题
上传~50MB文件时,会在目录/tmp/tomcat7-tomcat7-tmp
中创建两个具有相似MD5和的临时文件,名称分别为FileBackedOutputStream7949386530699987086.tmp
和MIME8234229766850016150.tmp
在上传完成之前,服务器抛出异常
javax.servlet.ServletException:org.lassfish.jersey.server.ContainerException:java.lang.OutOfMemoryError:java堆空间org.glassfish.jersy.servlet.WebComponent.service(WebComponent.java:421)org.glassfish.jersy.servlet.ServletContainer.service(ServletContainer.java:386)org.glassfish.jersy.servlet.ServletContainer.service(ServletContainer.java:335)org.glassfish.jersy.servlet.ServletContainer.service(ServletContainer.java:222)
并删除名称为CCD_ 4的文件而留下另一个文件。并且未删除的FileBackedOutputStream....tmp
文件占据了硬盘上的全部空间。
我做了什么
将堆空间增加到7GB,但约200MB的文件无法上传。
在服务器上运行作业以删除旧的临时文件。
已创建名为
jersey-multipart-config.properties
、内容为的文件jersey.config.multipart.bufferThreshold = -1
文件MIME[random numbers].tmp
不再创建,但除非tomcat重新启动,否则FileBackedOutputStream[random number].tmp
仍挂在硬盘上。
问题
Jersey如何处理大文件(可能是1GB)而不在我的硬盘上留下临时文件?最好的情况是根本不使用硬盘,而是通过内存传输小块。
如果输入流是用文件备份的,为什么会出现堆溢出?
我阅读的材料
- 到目前为止,我找到了最接近的解释。读取
- 这家伙也有类似的问题,但站在客户一边。读取
- 可能包含解决方案,但无法理解答案。读取
- 很接近我的问题,但无法解决。读取
- bufferThreshold的想法就是从这里得到的。读取
问题#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>
并删除了CCD_ 11文件。
现在我可以上传超过200Mb的文件。不再创建临时文件。
但我仍然无法解释问题2。