To illustrate the problem, suppose you need to process form data that include a file upload along with some other information, perhaps a keyword designation for the file. Your HTML <form> element might look something like this.
<form id="upload" enctype="multipart/form-data" method="post" action="upload">
    Keyword: <input name="keyword" /><br />
    File:<br/>
    <input type="file" name="file" size="44"/><br/>
    <input type="submit" value="Upload file" /><br />
</form>
And the (simplified) Java code you use to handle the form data might look like this.
@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public DataFile upload(
    @FormDataParam("keyword") String keyword,
    @FormDataParam("file") InputStream file_in,
    @FormDataParam("file") FormDataContentDisposition contentdisp)
    throws Exception {
    // Do something with the form data... 
}
That should all work fine, but what if you want to let users provide an arbitrary number of keywords, using a separate input box for each keyword? You could write some javascript to allow the user to click a "more keywords" button that adds more input boxes to the form. Then, your form would effectively look something like the following.
<form id="upload" enctype="multipart/form-data" method="post" action="upload">
    Keyword: <input name="keywords" /><br />
    Keyword: <input name="keywords" /><br />
    Keyword: <input name="keywords" /><br />
    <!-- Could be any number of keyword elements here... -->
    File:<br/>
    <input type="file" name="file" size="44"/><br/>
    <input type="submit" value="Upload file" /><br />
</form>
How should you handle this on the server side? The obvious solution is to specify a parameter that is a List, like this.
@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public DataFile upload(
    @FormDataParam("keywords") List<String> keywords,
    @FormDataParam("file") InputStream file_in,
    @FormDataParam("file") FormDataContentDisposition contentdisp)
    throws Exception {
    // Do something with the form data... 
}
Unfortunately, this straightforward approach doesn't work for MediaType.MULTIPART_FORM_DATA, even though it works fine for other media types. The trick is to replace the "keywords" parameter with a List of FormDataBodyPart objects. Then, we can extract the keyword String from each FormDataBodyPart. In the following example, the extracted keyword strings are placed into another List.
@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public DataFile upload(
    @FormDataParam("keywords") List<FormDataBodyPart> bparts,
    @FormDataParam("file") InputStream file_in,
    @FormDataParam("file") FormDataContentDisposition contentdisp)
    throws Exception {
    // Get the keyword strings.
    ArrayList<String> keywords = new ArrayList<String>(); 
    for (FormDataBodyPart bpart : keywords)
        keywords.add(bpart.getValueAs(String.class));
    // Do something with the form data... 
}
And that should work. It's a bit awkward, but still a fairly simple solution. Finally, I must acknowledge these two threads, which provided the clues I needed to figure this out.
Nice one! was going mental trying to process a List in multi-part form. thanks
ReplyDeleteYou bet! I'm glad somebody else found this useful.
DeleteHuge thanks! I've been looking for this solution for a couple hours.
ReplyDelete