users@jersey.java.net

Re: [Jersey] Documentation / Tutorial on using jersey-multipart

From: Craig McClanahan <Craig.McClanahan_at_Sun.COM>
Date: Tue, 16 Dec 2008 16:22:48 -0800

Paul Sandoz wrote:
> Hi Craig,
>
> I think your example reflects that we could do more to support
> multupart/form-data with the multipart module.
>
I've just checked in some initial support for more user-friendly
multipart/form-data handling. As you had suggested, subclasses
FormDataMultiPart and FormDataBodyPart provide convenient APIs to treat
the message as a set of named fields, rather than a set of independent
body parts.

As an example (similar to logic in the unit tests), let's say you have a
client that wants to send three fields -- two string fields and an
arbitrary JavaBean for which I have a MessageBodyWriter. The client
code, using Jersey client, could look something like this:

    WebResource service = ...;

    String name = "name value";
    String id = "id value";
    MyClass bean = ...;

    FormDataMultiPart = new FormDataMultiPart().
        field("name", name).
        field("id", id).
        field("bean", bean, MediaType.TEXT_XML_TYPE);
    ClientResponse = service.path("/path").
        type(MediaType.MULTIPART_FORM_DATA_TYPE).
        post(ClientResponse.class, multiPart);

Handling this at the server end might look something like this:

    @POST
    @Consumes("multipart/form-data")
    public Response post(FormDataMultiPart multiPart) {
        String name = multiPart.getField("name").getValue();
        String id = multiPart.getField("id").getValue();
        MyClass bean = multiPart.getFIeld("bean").getValueAs(MyClass.class);
        ... do something with this data ...
        return Response.ok().build();
    }

which is a *lot* simpler, on both ends, than the code to use
multipart/form-data without the specialized helpers.

There is one use case in RFC 2388 (the multipart/form-data) spec that I
didn't explicitly cover, but I'm not sure it needs anything extra.
That's when you want to send a single field as a set of files, instead
of just one. The spec says you should use a nested multipart/mixed to
contain the set, which you can do with the regular jersey-multipart
classes pretty easily.

By the way, if a client really *does* want to send the content of a
file, instead of a serialized bean, just set the field value to an
InputStream pointing at the content. JAX-RS implementations (including
Jersey) are required to support a Provider that just copies the bytes
from this stream, so yoiu don't have to do anything else special. At
the server end, you can acquire an InputStream reference to the uploaded
bytes something like this:

    InputStream stream = ((BodyPartEntity)
multiPart.getField("file").getEntitiy()).getInputStream();

and copy the bytes to wherever you want.

Craig