3

Quite a few links here that suggest using multipart/form-data and how to get the file upload here. Couldn't really find one for a combination of multiple files uploaded via CURL command and to a REST service accepting FormDataMultiPart.

The code in service currently looks like :

@javax.ws.rs.POST
@javax.ws.rs.Path("/sample-bulk")
@javax.ws.rs.Consumes(javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA)
public javax.ws.rs.core.Response bulkUpload(@FormDataParam("file") org.glassfish.jersey.media.multipart.FormDataMultiPart multiPart) {
    log.info("{} log", multiPart.getField("file"));
    return Response.ok().build();
}

and the CURL I am trying to call the service is as :

curl -X POST "http://localhost:37200/api/sample-bulk" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "file=@/Users/naman/Desktop/Media/video.mp4;type=video/mp4"

But it results in multiPart being null at the service and of course NPE.

Anything silly that I've missed here?

3
  • A sample suggestion online to use FormDataMultiPart as I start to wonder if this is even supported somehow. Commented Jul 9, 2019 at 18:15
  • 1
    You don't use @FormDataParam when you use FormDataMultiPart. FormDataMultiPart is the entire request body (all parts). You use @FormDataParam when you want to get individual parts, where you would use the part's name as the annotation value. Maybe this might provide some clarification. Also look at the Jersey docs for Multipart Commented Jul 10, 2019 at 6:02
  • @PaulSamsotha Thanks a lot for the response. I would look into the documentation in a while, though I could with the minimal example verify that removing the @FormDataParam works adding value to the multiPart. Commented Jul 10, 2019 at 6:42

3 Answers 3

4

The problem is with the method parameter (the presence of the @FormDataParam)

public Response bulkUpload(@FormDataParam("file") FormDataMultiPart multiPart) {}

@FormDataParam is used when you want to declaratively extract individual parts from the multipart request, whereas FormDataMultiPart is used to get an entire multipart body and programmatically extract each part. It's possible to have a nested multipart where a complete multipart is an individual part (in which case what you have would work), but this is not the case here.

If you remove the @FormDataParam("file"), then it will work as expected. You can start extracting parts out of the multipart using the method you are using getField(fieldName). This will give you a FormDataBodyPart for each part you extract. You can get the data with FormDataBodyPart#getValueAs(InputStream.class) if you want that part as an InputStream, or you can use File.class or byte[].class, whatever your preference. This is how to extract data from the FormDataMultiPart.

Each part has it's own name and you extract the part using that name. In the case of your cURL request, you sent one part, and the part's name is file. i.e. "file=@/Users/...". So if you want to send another part, just add another parameter with a different name1, as mentioned by Vladimir:

curl -X POST "http://localhost:37200/api/sample-bulk"\
     -H "accept: application/json"\
     -H "Content-Type: multipart/form-data"\
     -F "file1=@/Users/naman/Desktop/Media/video.mp4"\
     -F "file2=@/Users/naman/Desktop/Media/another_video.mp4"

As I mentioned earlier, @FormDataParam is used to extract parts declaratively. You use the part's name as the annotation value. So with the previous cURL command, you could do.

public Response bulkUpload(
        @FormDataParam("file1") InputStream file1,
        @FormDaraParam("file1") FormDataContentDisposition file1Fdcd,
        @FormDataParam("file2") InputStream file2,
        @FormDaraParam("file2") FormDataContentDisposition file2Fdcd) {
}

You can get information about the part, such as the file name from the FormDataContentDisposition.

See also


Footnotes

  1. Parts can have the same name also, e.g.

    -F file=@path_to_file1
    -F file=@path_to_file2
    

    This is the reason when you try to get a part programmatically, you get a list of FormDataBodyParts instead of a single object i.e

    FormDataMultiPart multiPart = ...
    List<FormDataBodyPart> files = multiPart.getField("file");
    

    And if you wanted to get them declaratively, you would use a List instead of a single object type

    public Response upload(@FormDataParam("file") List<InputStream> files) { ... }
    
Sign up to request clarification or add additional context in comments.

1 Comment

I use the way you want to handle multipart. I did formData.append('file', file.name, file.data); formData.append('testObj', JSON.stringyfy(testObj)) in service.ts file. And used multipart on REST to get "testObj" and "file" but getting NPE
2
Use this parameter 

public Response uploadFile(@FormDataParam("files") List<FormDataBodyPart> file) 

Use for loop

 for (int j = 0; j < files.size(); j++) {
   FormDataBodyPart this_formDataBodyPartFile = files.get(j);
   ContentDisposition this_contentDispositionHeader = this_formDataBodyPartFile
                                                    .getContentDisposition();
   InputStream this_fileInputStream = this_formDataBodyPartFile.getValueAs(InputStream.class);
                                            FormDataContentDisposition fileDetail = (FormDataContentDisposition) this_contentDispositionHeader;

//Write the code upload code   }

Comments

0

Try using separate -F flag for every multipart entry, something like that:

curl -X POST "http://localhost:37200/api/sample-bulk" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "file=@/Users/naman/Desktop/Media/video.mp4" -F "type=video/mp4"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.