How to avoid OutOfMemoryError when uploading a large file using Jersey client

 ̄綄美尐妖づ 提交于 2019-11-26 22:12:21

You could use streams.Try something like this on the client:

InputStream fileInStream = new FileInputStream(fileName);
String sContentDisposition = "attachment; filename=\"" + fileName.getName()+"\"";
WebResource fileResource = a_client.resource(a_sUrl);       
ClientResponse response = fileResource.type(MediaType.APPLICATION_OCTET_STREAM)
                        .header("Content-Disposition", sContentDisposition)
                        .post(ClientResponse.class, fileInStream);      

with resource like this on the server:

@PUT
@Consumes("application/octet-stream")
public Response putFile(@Context HttpServletRequest a_request,
                         @PathParam("fileId") long a_fileId,
                         InputStream a_fileInputStream) throws Throwable
{
    // Do something with a_fileInputStream
    // etc

In order for your code not to depend on the size of the uploaded file, you need:

  1. Use streams
  2. Define the chuck size of the jersey client. For example: client.setChunkedEncodingSize(1024);

Server:

    @POST
    @Path("/upload/{attachmentName}")
    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
    public void uploadAttachment(@PathParam("attachmentName") String attachmentName, InputStream attachmentInputStream) {
        // do something with the input stream
    }

Client:

    ...
    client.setChunkedEncodingSize(1024);
    WebResource rootResource = client.resource("your-server-base-url");
    File file = new File("your-file-path");
    InputStream fileInStream = new FileInputStream(file);
    String contentDisposition = "attachment; filename=\"" + file.getName() + "\"";
    ClientResponse response = rootResource.path("attachment").path("upload").path("your-file-name")
            .type(MediaType.APPLICATION_OCTET_STREAM).header("Content-Disposition", contentDisposition)
            .post(ClientResponse.class, fileInStream);
rschmidt13

Below is the code for uploading a (potentially large) file with chunked transfer encoding (i.e. streams) using Jersey 2.11.

Maven:

<properties>
    <jersey.version>2.11</jersey.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-client</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
    <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-multipart</artifactId>
        <version>${jersey.version}</version>
    </dependency>
<dependencies>

Java:

Client client = ClientBuilder.newClient(clientConfig);
client.property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED");

WebTarget target = client.target(SERVICE_URI); 
InputStream fileInStream = new FileInputStream(inFile);
String contentDisposition = "attachment; filename=\"" + inFile.getName() + "\"";
System.out.println("sending: " + inFile.length() + " bytes...");
Response response = target
            .request(MediaType.APPLICATION_OCTET_STREAM_TYPE)
            .header("Content-Disposition", contentDisposition)
            .header("Content-Length", (int) inFile.length())
            .put(Entity.entity(fileInStream, MediaType.APPLICATION_OCTET_STREAM_TYPE));
System.out.println("Response status: " + response.getStatus());
xonya

In my case (Jersey 2.23.2) rschmidt13's solution gave this warning:

WARNING: Attempt to send restricted header(s) while the [sun.net.http.allowRestrictedHeaders] system property not set. Header(s) will possibly be ignored.

This can be solved adding the following line:

System.setProperty("sun.net.http.allowRestrictedHeaders", "true");

However I think a cleaner solution can be obtained using the StreamingOutput interface. I post a complete example hoping it could be useful.

Client (File upload)

WebTarget target = ClientBuilder.newBuilder().build()
            .property(ClientProperties.CHUNKED_ENCODING_SIZE, 1024)
            .property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED")
            .target("<your-url>");

StreamingOutput out = new StreamingOutput() {

    @Override
    public void write(OutputStream output) throws IOException, 
            WebApplicationException {

        try (FileInputStream is = new FileInputStream(file)) {

            int available;
            while ((available = is.available()) > 0) {
                // or use a buffer
                output.write(is.read());
            }
        }
    }
};

Response response = target.request().post(Entity.text(out));

Server

@Path("resourcename")
public class MyResource {

    @Context
    HttpServletRequest request;

    @POST
    @Path("thepath")
    public Response upload() throws IOException, ServletException {

        try (InputStream is = request.getInputStream()) {
            // ...
        }
    }
}

If possible, can you split the file you send into smaller parts? This will reduce memory usage, but you need to change the code on both sides of the uploading/downloading code.

If you can't, then your heap space is too low, try increasing it with this JVM parameter. In your application server add/change the Xmx JVM options. For example

-Xmx1024m

to set Max Heap Space to 1Gb

EL missaoui habib
@Consumes("multipart/form-data")
@Produces(MediaType.TEXT_PLAIN + ";charset=utf-8")
public String upload(MultipartFormDataInput input,  @QueryParam("videoId") String  videoId,
        @Context HttpServletRequest a_request) {

    String fileName = "";
    for (InputPart inputPart : input.getParts()) {
        try {

            MultivaluedMap<String, String> header = inputPart.getHeaders();
            fileName = getFileName(header);
            // convert the uploaded file to inputstream
            InputStream inputStream = inputPart.getBody(InputStream.class, null);               
            // write the inputStream to a FileOutputStream
            OutputStream outputStream = new FileOutputStream(new File("/home/mh/Téléchargements/videoUpload.avi"));
            int read = 0;
            byte[] bytes = new byte[1024];
            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
            System.out.println("Done!");
        } catch (IOException e) {
            e.printStackTrace();
            return "ko";
        }

    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!