barmatat barmatat - 4 years ago 207
Java Question

Uploading large file using Jersey on backend

I run a simple backend app which allows to upload files. I use Jersey and run it in Jetty. The piece of my code looks like this:

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@Context UriInfo uriInfo, FormDataMultiPart multipart, @Context HttpServletRequest httpRequest) {
FormDataBodyPart fileId = multipart.getField("fileId");
FormDataBodyPart fileSize = multipart.getField("fileSize");
FormDataBodyPart file = multipart.getField("file");

ContentDisposition cd = file.getContentDisposition();
String fileName = cd.getFileName();
long size = Long.valueOf(fileSize.getValue());
...


Upload works just fine, but I found that the method is called just when the whole stream is uploaded to the backend. So, for instance, if I send huge file (3Gigs to be uploaded) my POST request immediately appears on the backend, but the method above is invoked only when the whole 3 Gigs are uploaded through the network.

I would like to make some checks in the method and don't upload the file for some cases, so it doesn't need to pass the whole content to the backend and then send the error message back.

How can I avoid uploading the whole file content to the backend but make the method is invoked before I start to read from the data stream?

Answer Source

Eventually I worked it around with 2 sequential POSTs the client should make to upload such big files:

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response newUpload(@Context UriInfo uriInfo, FormDataMultiPart multipart,
        @Context HttpServletRequest httpRequest) {
    FormDataBodyPart fileSize = multipart.getField("fileSize");
    long size = Long.valueOf(fileSize.getValue());
    if (!checkSizeLimits(size)) {
        return Response.status(Status.BAD_REQUEST).build();
    }
    // here comes some code which generates an unique id and its URI for the data
    ...
    return Response.status(Status.CREATED).entity(new FileUploadInfo(uri.toString())).build();
}

@POST
@Path("/{fileId}")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@Context UriInfo uriInfo, FormDataMultiPart multipart,
        @Context HttpServletRequest httpRequest, @PathParam("fileId") String fileId) {
    FormDataBodyPart fileSize = multipart.getField("fileSize");
    FormDataBodyPart file = multipart.getField("file");
    //...
    return Response.status(Status.CREATED).entity(new FileUploadInfo(uri.toString())).build();
 }

It looks ugly, but better than to do the file size check after the data stream is completely loaded. So, client should make POST call to the newUpload() method first providing the file size. If the size is ok, the response contains URI for the second POST to stream the file data. The second method can do the file size check again (not provided), to be sure that the initial POST had the same file size field.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download