For those who don't know me, I have been around the Java web tier for
quite a while, being the original author of the Struts framework
(<
http://struts.apache.org>), as well as co-spec-lead for JavaServer
Faces 1.0 and 1.1. My more recent interests have focused on RESTful web
services, which led me naturally towards JAX-RS and the Jersey
implementation.
I've been one of the folks inside Sun who has been leveraging Jersey for
some internal projects over the last few months. We had a particular
need to support MIME multipart/* media types, and it made sense to
generalize this into a reusable module -- hence, I've just uploaded the
"jersey-multipart" module to the "contribs" directory. It relies on 1.0
or later Jersey code, and provides what I hope are found to be elegant
solutions to the problems of multipart handling, while leveraging all
the nice JAX-RS providers support for dealing with the entity content of
body parts, just like we've grown spoiled by on complete message
bodies. And, it works both on the server side and the client side, when
you use jersey-client.
Example server code to build a multipart response might look like this:
// I have also provided an appropriate MessageBodyWriter for the
MyBean class
MyBean bean = ...;
return Response.ok(new MultiPart().
type(new MediaType("multipart", "mixed").
bodyPart("This is the first body part in plain text", new
MediaType("text", "plain")).
bodyPart(bean, new MediaType("x-application", "x-format"))).build();
(Of course, you can do things in a more fine-grained fashion, but the
builder pattern utilized all over the JAX-RS and Jersey APIs was so cool
that Paul suggested I use it here too, so I did :-).
To read a MultiPart entity (produced by code like the previous example)
that was uploaded to the server you might do something like this:
// I have also provided an appropriate MessageBodyReader for the
MyBean class
@Path("...")
@PUT
@Consumes("multipart/mixed")
@Produces(...)
public Response handler(MultiPart multiPart) {
BodyPart part0 = multiPart.getBodyParts().get(0);
String text = part0.getEntityAs(String.class);
BodyPart part1 = multiPart.getBodyParts().get(1);
MyBean bean = part1.getEntityAs(MyBean.class);
...
multiPart.cleanup();
}
The need for cleanup() is because the implementation knows how to buffer
"large" body parts to temporary files on disk, so you don't blow away
your JVM heap on a multi-gigabyte upload or download. I'm looking for a
way to avoid the need for the application to call this, but haven't
found one yet -- in the mean time, everything else about dealing with
multipart files has seemed pretty easy to deal with.
As mentioned above, this module works on the client side as well, if
you're using jersey-client. The unit tests have some more worked-out
examples of the lower level details.
Give it a try and see what you think! And, for sure, if you see
anything that needs to be improved, please ask here and/or file an issue
in the issue tracking system.
Craig McClanahan
PS: Among my other interests will be working with the Atom Publishing
Protocol support, again with the idea of leveraging JAX-RS providers to
do format translations for custom <content> payloads.