Feb 25

This post explains how to post multipart form-data to a URI using Apache Wink. For the sake of this tutorial, I will demonstrate how to upload an image to Facebook, using the Graph API. Note: there is no access_token information.

The code snippet below presents how to create the request entity for multipart/form-data posting:

1
2
3
4
5
6
7
8
9
10
File file = new File("/path/file.jpg");
String fileName = file.getName();

BufferedOutMultiPart requestEntity = new BufferedOutMultiPart();
OutPart outPart = new OutPart();
outPart.setContentType("application/octet-stream; name=" + fileName);
outPart.setBody(bytes);
outPart.addHeader("Content-Transfer-Encoding", "binary");
outPart.addHeader("Content-Disposition", "form-data; name=\"" + fileName + "\"; filename=\"" + fileName + "\"");
requestEntity.addPart(outPart);

Using the class org.apache.wink.common.model.multipart.BufferedOutMultiPart as the request entity, one can include as many parts as wanted. Each part is abstracted as an instance of org.apache.wink.common.model.multipart.OutPart, which can represent any kind of information. The code snippet below demonstrate how to add a text part:

1
2
3
4
5
6
7
8
9
String stringFieldValue = "stringField";
String stringFieldName = "stringValue";

outPart = new OutPart();
outPart.setContentType("text/plain; charset=us-ascii");
outPart.setBody(URLEncoder.encode(stringFieldValue, "UTF-8"));
outPart.addHeader("Content-Transfer-Encoding", "7bit");
outPart.addHeader("Content-Disposition", "form-data; name=\"" + stringFieldName + "\"");
requestEntity.addPart(outPart);

The code snippet below posts the multipart data to the server (uploading the image to Facebook):

1
2
3
String string = new RestClient().resource("https://graph.facebook.com/me/photos").
contentType(MediaType.MULTIPART_FORM_DATA_TYPE).
post(String.class, requestEntity);

The following output (headers + body) will be generated by Apache Wink and sent to the server:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Accept: */*
Content-Type: multipart/form-data
User-Agent: Wink Client v1.1.2

--simple boundary
Content-Disposition: form-data; name="file.jpg"; filename="file.jpg"
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream; name=file.jpg

...
--simple boundary
Content-Disposition: form-data; name="stringField"
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=us-ascii

stringValue
--simple boundary--

Pretty simple, huh? Let’s undestand how it works…

The class org.apache.wink.common.internal.providers.multipart.OutMultiPartProvider is provider responsible for writing the parts to the server. This class leverages the writePart and writeBody methods defined in org.apache.wink.common.model.multipart.OutPart class. Besides, it performs a very important step: including the boundary information in the request header, as you can see at line 2 in the above code…. no, wait a second, it is not there! The expected result (for line 2) is:

1
Content-Type: multipart/form-data; boundary=simple boundary

The default boundary value Apache Wink uses is ‘simple boundary‘. When the provider is executing (look at its writeTo method), it has two options: (1) use an already provided boundary value or (2) use the default one. When using the default one, it updates the request header in order to include the boundary information (generating the expected result as displayed in the box above). However, by the time the header is set, it has already been sent to the server (ie the connection was already established and the headers sent). So, considering the way it is implemented nowadays, the default boundary value will never work!

In the other hand, when you specify the boundary manually as a request header, you will guarantee the proper header is sent to the server and the multipart provider will use the provided boundary information. In order to do it, use instead the code below for posting the request:

1
2
3
4
String boundary = "simple boundary";
String string = restClient.resource(url).
header("Content-Type", "multipart/form-data; boundary=" + boundary).
post(String.class, requestEntity);

The multipart provider will use boundary parameter specified with the Content-Type header. Instead of using ’simple boundary’, please follow the recommendation of RFC 1867: “a boundary is selected that does not occur in any of the data”.

Conclusion: Apache Wink’s current multipart implementation is incomplete. Either they make possible for providers to change the request headers prior to establishing the connection between client and server, or they make the boundary definition mandatory, throwing an exception when not present.

The following issue was raised to Wink’s JIRA: https://issues.apache.org/jira/browse/WINK-338

Related posts

Tagged with:
preload preload preload