sarat.pediredla wrote:
> Hi,
>
> I have been trawling the mailing list for the past hour trying to find a
> solution to this and could not, so I gave up and thought I might just ask.
>
> I have a REST API, which work's great when serialising from and to JSON/XML.
> This serves me great but now I want to start accepting attachments
> (primarily binary images) through the REST API and wanted to see how this
> could be achieved.
>
> Here is a sample requirement,
>
> "I want to be able to POST a note, along with an attachment to the API and
> have it create the Note (which is a JAXB Bean) and save the attachment to
> file, assigning the disk file path to Note.setAttachmentPath(String
> filePath))"
>
> The problem is being able to POST both the Note object and binary attachment
> in 1 request, so I figured the only easy way for me to get this to work is
> use @Consumes("multipart/form-data").
>
> I hear I could use jersey-multipart to handle this, but I can find no
> documentation on how this will work. Ideally, I would like something like
> this,
>
> @POST
> @Consumes("multipart/form-data")
> public String postNote(
> Note note,
> File file)
> throws Exception {
> ..
> }
>
> I saw another note from Paul Sandoz somewhere which mentions the use of
> FormDataContentDisposition like below,
>
> @POST
> @Consumes("multipart/form-data")
> public String post(
> @FormParam("file") JAXBBean f,
> @FormParam("file") FormDataContentDisposition fdc)
> throws Exception {
> ..
> }
>
> However, this doesn't really explain how to use the jersey-multipart request
> to read the file and save it (I would prefer to use Apache Commons to save
> the file to disk, like I do in my struts2 actions)
>
> Any help?
>
Jersey-multipart was indeed designed for things like this, but doesn't
include the "copy a file" part directly, although that is pretty easy to
implement. Let's presume that your client sends a "multipart/mixed"
message, rather than "multipart/form-data" -- I'll show some sample
client code for that further down -- where the first part is your Note
object (serialized with JAXB), and the second part is the binary
attachment. On the second part, the client can send whatever media type
it wants, but we're going to assume it's a binary data stream and not
try to parse it -- we just want to store it.
The server side might look something like this:
@POST
@Consumes("multipart/mixed")
public Response post(MultiPart multiPart) {
// First part contains a Note object serialized by JAXB, so pull
it back out
Note note = multiPart.getBodyParts().get(0).getEntityAs(Note.class);
// We do not know what type the second part is, so get an input
stream
// to the content so we can save it to disk
BodyPartEntity bpe = (BodyPartEntity)
multiPart.getBodyParts().get(1).getEntity();
InputStream stream = bpe.getInputStream();
// Now we can copy the bytes from this body part entity to
wherever we want
...
}
If your client uses Jersey, you can also use jersey-multipart to
construct the request to be sent, using code something like this:
WebResource service = ...; // Create a WebResource instance pointing
at your web service
Note note = ...; // Note object for the first body part
byte[] data = ...; // Byte array containing the data for the attachment
// Construct a MultiPart containing the two body parts with our data
MultiPart multiPart = new MultiPart().
bodyPart(new BodyPart(note, MediaType.APPLICATION_XML_TYPE)).
bodyPart(new BodyPart(data,
MediaType.APPLICATION_OCTET_STREAM_TYPE));
// Use a POST to send this as a single request
ClientResponse response =
service.path("/myservice").type("multipart/mixed").post(ClientResponse.class,
multiPart);
...
It's a little more complicated if the client side data is really a disk
file and is potentially too big to fit in memory. In that case, you'll
need to provide some sort of Java class for the second body part's
entity (perhaps configured with the file path), along with a JAX-RS
MessageBodyWriter that knows how stream the bytes from the file out to
the output stream. Providing such gadgets would make a nice enhancement
to jersey-client (they would be useful outside the context of just
jersey-multipart) ... I will do some thinking of what general support
for this might look like.
Hope this helps.
Craig