Play 2.0 How to Post MultipartFormData using WS.url or WS.WSRequest

This is sloppy, and can definitely be cleaned up, but here's what I did to get it working. Feel free to make this so much better.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import play.libs.WS;

import com.ning.http.multipart.FilePart;
import com.ning.http.multipart.MultipartRequestEntity;
import com.ning.http.multipart.Part;

ByteArrayOutputStream bos = new ByteArrayOutputStream();

// Build up the Multiparts
List<Part> parts = new ArrayList<>();
parts.add(new FilePart("file", new File(filename)));
Part[] partsA = parts.toArray(new Part[parts.size()]);

// Add it to the MultipartRequestEntity
MultipartRequestEntity reqE = new MultipartRequestEntity(partsA, null);
reqE.writeRequest(bos);
InputStream reqIS = new ByteArrayInputStream(bos.toByteArray());
WS.WSRequestHolder req = WS.url(InterchangeConfig.conflateUrl+"dataset")
    .setContentType(reqE.getContentType());
req.post(reqIS).map(...);
// or req.post(reqIS).get();

This is all using pieces already in the Play 2.0 framework.


The only solution for now, without relying to external libraries, seems to be creating the Multipart Form Data request manually. This is an example how it can be done, using play.libs.WS.url:

WSRequestHolder wsRequestHolder = WS.url(URL);

String boundary = "--XYZ123--";

String body = "";
for (String key : data.keySet()) {
  body += "--" + boundary + "\r\n"
       + "Content-Disposition: form-data; name=\""
       + key + "\"\r\n\r\n"
       + data.get(key) + "\r\n";
}
body += "--" + boundary + "--";

wsRequestHolder.setHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
wsRequestHolder.setHeader("Content-length", String.valueOf(body.length()));

wsRequestHolder.post(body);

data would be a java.util.Map<String, String> containing all the name/value pairs you want to pass as form parameters. randomString is a randomized value to make the boundary change from request to request. Adding binary data would work the same way.

This http://www.htmlcodetutorial.com/forms/form_enctype.html is a good place to refer to for understanding the specs.


Working example for play 2.3 using above approach, also added contentType while uploading the file.

public Promise<WSResponse> upload(Http.MultipartFormData.FilePart policyFilePart, String contentType) {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    List<Part> parts = new ArrayList<>();
    try {
        parts.add(new FilePart("file", policyFilePart.getFile(), contentType, null));
        parts.add(new StringPart("param1", "value1"));
        parts.add(new StringPart("param2", "value2"));
        Part[] partsA = parts.toArray(new Part[parts.size()]);

        // Add it to the multipart request entity
        MultipartRequestEntity requestEntity = new MultipartRequestEntity(partsA, new FluentCaseInsensitiveStringsMap());
        requestEntity.writeRequest(bos);
        InputStream reqIS = new ByteArrayInputStream(bos.toByteArray());
        return WS.url(baseUrl + "upload")
                .setContentType(requestEntity.getContentType())
                .post(reqIS).map(new Function<WSResponse, WSResponse>() {
                    @Override
                    public WSResponse apply(WSResponse wsResponse) throws Throwable {
                            return wsResponse;
                    }
                });
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}