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

In Java Http request, we can do this to make multipart HTTP POST.

HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);

FileBody bin = new FileBody(new File(fileName));
StringBody comment = new StringBody("Filename: " + fileName);

MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("bin", bin);
reqEntity.addPart("comment", comment);
httppost.setEntity(reqEntity);

HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();

How could I achieve the same using WS.url or WS.WSRequest?

WSRequestHolder wsReq = WS.url("http//url");            
wsReq.setHeader("Content-type", "multipart/form-data");

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.

Scala WS - 2.8.x, To build an HTTP request, you start with ws.url() to specify the URL. val request: WSRequest To post multipart-form-encoded data a Source[play.api.mvc. Is this bug fixed yet ? I am trying to post a photo to facebook using WS play. On Wednesday, May 23, 2012 4:28:21 AM UTC-5, Mikael Ståldal wrote: How can I make a POST request to an external Web Service with a multipart/form-data body? Using play.api.lib.ws.WS from Scala. --

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.

Java WS - 2.8.x, To build an HTTP request, you start with ws.url() to specify the URL. This returns a WSRequest that you can use to specify various HTTP options, such as setting headers. The easiest way to post multipart/form data is to use a Source<​Http.MultipartFormData. Play Framework is released under the Apache 2 License. How to make a post call in play framework with request data as multipart data using WsClient. I am trying this but its not working.I have to make a post call with

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;
    }
}

Provide a post() method on WS for POSTing multipart/form-data , It's sad to not have a WS method which allow to call a controller like the "File upload example" asInstanceOf[AsyncHttpClient] def url(url: String): MultiPartWSRequestHolder = { val builder jroper added the help wanted label on Jun 2, 2015 Fixes #902 added a way to sent multipart/form-data requests via play-ws #5698. The Play WS API extends Standalone WS post with Http.Multipart and Multipart types that are only available in Play, for example: def withBody(body: Source[MultipartFormData.Part[Source[ByteString, _]], _]): Self

It seems, based on play API documentation, that there is no built-in for multipart POST bodies.

However, it may be possible to create your own multipart body using the method

post[T](body: T)(implicit wrt: Writeable[T], ct: ContentTypeOf[T]): Future[Response]

with a type T of your choice, and the corresponding Writeable and ContentTypeOf types too.

But this would imply digging in how multipart bodies work with HTTP.

‍ Play 2.0 How to send MultipartFormData using WS.url or WS , Play 2.0 How to send MultipartFormData using WS.url or WS.WSRequest In the Java Http request, we can do this to do a multipart HTTP POST. HttpCli To post multipart-form-encoded data a Source[play.api.mvc.MultipartFormData.Part[Source[ByteString, Any]], Any] needs to be passed into post. ws.url(url).post(Source.single(DataPart("key", "value"))) To upload a file you need to pass a play.api.mvc.MultipartFormData.FilePart[Source[ByteString, Any]] to the Source :

As Romain Sertelon suggested, you can write a Writeable to handle this case. Here's one I wrote:

package utilities

import java.io.{ByteArrayOutputStream, File}

import com.ning.http.client.FluentCaseInsensitiveStringsMap
import com.ning.http.multipart.{MultipartRequestEntity, FilePart, StringPart}
import play.api.http.HeaderNames._
import play.api.http.{ContentTypeOf, Writeable}
import play.api.mvc.{Codec, MultipartFormData}

object MultipartFormDataWriteable {

    implicit def contentTypeOf_MultipartFormData[A](implicit codec: Codec): ContentTypeOf[MultipartFormData[A]] = {
        ContentTypeOf[MultipartFormData[A]](Some("multipart/form-data; boundary=__X_PROCESS_STREET_BOUNDARY__"))
    }

    implicit def writeableOf_MultipartFormData(implicit contentType: ContentTypeOf[MultipartFormData[File]]): Writeable[MultipartFormData[File]] = {
        Writeable[MultipartFormData[File]]((formData: MultipartFormData[File]) => {

            val stringParts = formData.dataParts flatMap {
                case (key, values) => values map (new StringPart(key, _))
            }

            val fileParts = formData.files map { filePart =>
                new FilePart(filePart.key, filePart.ref, filePart.contentType getOrElse "application/octet-stream", null)
            }

            val parts = stringParts ++ fileParts

            val headers = new FluentCaseInsensitiveStringsMap().add(CONTENT_TYPE, contentType.mimeType.get)
            val entity = new MultipartRequestEntity(parts.toArray, headers)
            val outputStream = new ByteArrayOutputStream
            entity.writeRequest(outputStream)

            outputStream.toByteArray

        })(contentType)
    }

}

Here's how to use it:

import utilities.MultipartFormDataWriteable._

...

val url = "https://example.com"

val dataParts = Map(
    "foo" -> Seq("bar"),
    "alice" -> Seq("bob")
)

val file = new jave.io.File(... path to a jpg ...)
val fileParts = Seq(new FilePart("attachment", "foo.jpg", Some("image/jpeg"), file)

val multipartFormData = MultipartFormData(dataParts, fileParts, Seq(), Seq())

WS.url(url).post(multipartFormData)

Play 2.0 How to Post MultipartFormData using WS.url or - html, Play 2.0 How to Post MultipartFormData using WS.url or WS.WSRequest - playframework-2.0. §The Play WS API. Sometimes we would like to call other HTTP services from within a Play application. Play supports this via its WS library, which provides a way to make asynchronous HTTP calls.

2. The Play WebService (WS) Library, Asynchronous HTTP Programming with Play Framework 2. 3. 4. 5. 6. ws.url(url) .thenAccept(r ->. log.debug( "Thread#" + Thread. such as GET or POST, and multipart requests for file upload. 4.1. Initialize the WSRequest Object A multipart form requires us to send both input fields and data from an  3 Play 2.0 How to Post MultipartFormData using WS.url or WS.WSRequest Apr 5 '17 2 Testing sails controller Aug 25 '14 1 Is it possible to create group chat dialog/room from the server instead of client?

file upload to remote server using play framework, Am using play 2.2.0 with Scala 2.10.2 and sbt 0.13.11 starting with Play 2.5, since now multipart file upload is fully supported in Play WS. val request1: WSRequest = ws.url("actual url").post(Source(FilePart("hello", "hello.txt", -​multipart/form-data should probably include an example with all the imports. So I thought, to upload files using FormData, it would be enough to override it with: 'Content-Type': 'multipart/form-data' But alas, it didn’t work, server couldn’t parse the files I was uploading. I’ve wasted about half an hour, and then noticed that simple HTML form was setting something else:

WS.WSRequest (Play! API), WSRequest. java.lang.Object extended by play.libs.WS.WSRequest. Direct Known Subclasses: WSAsync.WSAsyncRequest, WSUrlFetch.​WSUrlfetchRequest. §Calling REST APIs with Play WS. Sometimes we would like to call other HTTP services from within a Play application. Play supports this via its WS library, which provides a way to make asynchronous HTTP calls.

Comments
  • This worked for us in Play 2.2, but seems to have broken in Play 2.3 ... even after fixing the play.libs.ws change. Appreciate any guess why.
  • @EricWilson can you be more specific than "broken"?
  • Sorry, i don't work with play any more, and don't have access to that code.
  • Looking at source for MultipartRequestEntity looks like it will blow up if you pass null for the headers
  • Something similar worked for me in scala. For 2nd ctor param I write new FluentCaseInsensitiveStringsMap, and I post like: .post(baos.toByteArray())(Writeable.wBytes, ContentTypeOf(Some(mpre.getContentType)))
  • A link to a solution is welcome, but please ensure your answer is useful without it: add context around the link so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. Answers that are little more than a link may be deleted.
  • @M.A.R. I have removed the link, instead I have written the code here only
  • @live_alone I'm trying to use this in Play 2.4 but it is resulting in 400 due to Unexpected end of input
  • Could you share an example?
  • Actually, I've shown you the Scala API, and you seem to use Java, sorry. In Java, there's a post(InputStream) method. Maybe you can create the right content in an input stream then :)
  • @angelokh: Here's an example: cdmckay.org/blog/2015/06/24/…
  • Really great idea! It no longer compiles, though. Did you mean import com.ning.http.client.multipart ...? Also, stringParts ++ fileParts adds a StringPart to a Seq[FilePart], leading to a Seq[BasePart]. MultipartRequestEntity requires a util.List[Part] (not an Array[PartBase]). Does it compile in the latest Play release 2.4.6 for you?