Span | ||
---|---|---|
| ||
JAX-RS : Support for Multiparts |
Table of Contents |
---|
Reading attachments
...
For example:
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
@Path("/books/jaxbjson")
@Produces("text/xml")
public Response addBookJaxbJson(
@Multipart(value = "rootPart", type = "text/xml") Book2 b1,
@Multipart(value = "book2", type = "application/json") Book b2)
throws Exception {
}
|
...
All attachment parts can be accessed as a list of Attachment with Attachment making it easy to deal with a given part:
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
public void addAttachments(List<Attachment> atts) throws Exception {
}
|
...
Similarly, the whole request body can be represented as a MultipartBody:
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
public void addAttachments(MultipartBody body) throws Exception {
body.getAllAtachments();
body.getRootAttachment();
}
|
When handling complex multipart/form-data submissions (such as those containing files) MultipartBody (and Attachment) need to be used directly. In simpler cases, when every form part can be captured by a String, the following code will suffice:
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
@Consumes("multipart/form-data")
public void addForm1(@FormParam("name") String title, @FormParam("id") Long id) throws Exception {
}
@POST
@Consumes("multipart/form-data")
public void addForm2(@FormParam("") BookBean book) throws Exception {
}
@POST
@Consumes("multipart/form-data")
public void addForm3(MultivaluedMap<String, String> formData) throws Exception {
}
|
When working with either List of Attachments or MultipartBody, one may want to process the individual parts with the help of some custom procedures. Starting from CXF 2.3.0 it is also possible to do the following:
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
public void addAttachments(MultipartBody body) throws Exception {
Book book = body.getAttachmentObject("bookPart", Book.class);
}
@POST
public void addAttachments(List<Attachment> attachments) throws Exception {
for (Attachment attachment : attachments) {
Book book = attachment.getObject(Book.class);
}
}
|
...
When you write the code like this
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
@Path("/books/jaxbjson")
@Produces("text/xml")
public Response addBookJaxbJson(
@Multipart("rootPart") Book2 b1,
@Multipart("book2") Book b2) {}
|
the runtime will return a 400 status if either "rootPart" or "book2" parts can not be found in the multipart payload.
Starting from 2.5.1 it is possible to request the runtime to report a null value in case of missing parts:
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
@Path("/books/jaxbjson")
@Produces("text/xml")
public Response addBookJaxbJson(
@Multipart("rootPart") Book2 b1,
@Multipart(value = "book2", required = false) Book b2) {}
|
...
On the server side it is sufficient to update the @Produces value for a given method:
Code Block | ||||
---|---|---|---|---|
| ||||
public class Resource {
private List<Book> books;
@Produces("multipart/mixed;type=text/xml")
public List<Book> getBooksAsMultipart() {
return booksList;
}
@Produces("multipart/mixed;type=text/xml")
public Book getBookAsMultipart() {
return booksList;
}
}
|
...
When returning mixed multiparts containing objects of different types, you can either return a Map with the media type string value to Object pairs or MultipartBody:
Code Block | ||||
---|---|---|---|---|
| ||||
public class Resource {
private List<Book> books;
@Produces("multipart/mixed")
public Map<String, Object> getBooks() {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("text/xml", new JaxbBook());
map.put("application/json", new JSONBook());
map.put("application/octet-stream", imageInputStream);
return map;
}
@Produces("multipart/mixed")
public MultipartBody getBooks2() {
List<Attachment> atts = new LinkedList<Attachment>();
atts.add(new Attachment("root", "application/json", new JSONBook()));
atts.add(new Attachment("image", "application/octet-stream", getImageInputStream()));
return new MultipartBody(atts, true);
}
}
|
...
You can also control the contentId and the media type of the root attachment by using a Multipart annotation:
Code Block | ||||
---|---|---|---|---|
| ||||
public class Resource {
@Produces("multipart/form-data")
@Multipart(value = "root", type = "application/octet-stream")
public File testGetImageFromForm() {
return getClass().getResource("image.png").getFile();
}
}
|
...
On the client side multiparts can be written the same way. For example:
Code Block | ||||
---|---|---|---|---|
| ||||
WebClient client = WebClient.create("http://books");
client.type("multipart/mixed").accept("multipart/mixed");
List<Attachment> atts = new LinkedList<Attachment>();
atts.add(new Attachment("root", "application/json", new JSONBook()));
atts.add(new Attachment("image", "application/octet-stream", getImageInputStream()));
List<Attachment> atts = client.postAndGetCollection(atts, Attachment.class);
|
...
When using proxies, a Multipart annotation attached to a method parameter can also be used to set the root contentId and media type. Proxies do not support at the moment multiple method parameters annotated with Multipart (as opposed to the server side) but only a single multipart parameter:
Code Block | ||||
---|---|---|---|---|
| ||||
public class Resource {
@Produces("multipart/mixed")
@Consumes("multipart/form-data")
@Multipart(value = "root", type = "application/octet-stream")
public File postGetFile(@Multipart(value = "root2", type = "application/octet-stream") File file) {}
}
|
A method-level Multipart annotation will affect the writing on the server side and the reading on the client side. A parameter-level Multipart annotation will affect writing on the client (proxy) side and reading on the server side. You don't have to use Multipart annotations.
Uploading files with Client API
At the moment the only way to upload a file is to use a MultipartBody, Attachment or File:
Code Block | ||||
---|---|---|---|---|
| ||||
WebClient client = WebClient.create("http://books");t
client.type("multipart/form-data");
ContentDisposition cd = new ContentDisposition("attachment;filename=image.jpg");
Attachment att = new Attachment("root", imageInputStream, cd);
client.post(new MultipartBody(att));
// or just post the attachment if it's a single part request only
client.post(att);
// or just use a file
client.post(getClass().getResource("image.png").getFile());
|
...
One can use the following properties to set up folder, memory threshold and max size (from CXF 2.4.4 and 2.5) values when dealing with large attachments:
Code Block | ||||
---|---|---|---|---|
| ||||
<beans>
<jaxrs:server id="bookstore1">
<jaxrs:properties>
<entry key="attachment-directory" value="/temp/bookstore1"/>
<!-- 200K-->
<entry key="attachment-memory-threshold" value="404800"/>
<entry key="attachment-max-size" value="404800"/>
</jaxrs:properties>
</jaxrs:server>
</beans>
|
...
For example this request can be handled by a method with the following signature:
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
@Path("/books/jsonform")
@Consumes("multipart/form-data")
public Response addBookJsonFromForm(Book b1) {...}
|
Similarly, this request can be handled by a method with the following signature:
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
@Path("/books/jsonjaxbform")
@Consumes("multipart/form-data")
public Response addBookJaxbJsonForm(@Multipart("jsonPart") Book b1,
@Multipart("jaxbPart") Book b2) {}
|
...
Finally, multipart/form-data requests with multiple files (file uploads) can be supported too. For example, this request can be handled by a method with the signature like :
Code Block | ||||
---|---|---|---|---|
| ||||
@POST
@Path("/books/filesform")
@Produces("text/xml")
@Consumes("multipart/form-data")
public Response addBookFilesForm(@Multipart("owner") String name,
@Multipart("files") List<Book> books) {}
|
...
Please note that using JAX-RS FormParams is recommended for dealing with plain application/www-url-encoded submissions consisting of name/value pairs only.
Content-Disposition UTF-8 file names
Starting from CXF 3.0.4 it is possible to specify a Content-Disposition file names in a UTF-8 format, using a "filename*" Content-Disposition extension parameter as opposed to the "filename" one.
Please see RFC 6266 and this unit test for more information.
XOP support
CXF JAXRS clients and endpoints can support XML-binary Optimized Packaging (XOP).
What it means at a practical level is that a JAXB bean containing binary data is serialized using a multipart packaging, with the root part containing non-binary data only but also linking to co-located parts containing the actual binary payloads. Next it is deserialized into a JAXB bean on the server side.
...
One option is to let Struts2 handle URIs matching some specific patterns only, for example:
Code Block | ||||
---|---|---|---|---|
| ||||
<web-app>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
</web-app>
|
...