2012-06-05 12 views
8

In Java-HTTP-Anfrage können wir dies tun, um mehrteilige HTTP POST zu machen.Play 2.0 So veröffentlichen Sie MultipartFormData mit WS.url oder WS.WSRequest

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(); 

Wie kann ich dasselbe mit WS.url oder WS.WSRequest erreichen?

Antwort

5

Dies ist schlampig, und kann auf jeden Fall gereinigt werden, aber hier ist, was ich getan habe, um es zum Laufen zu bringen. Fühlen Sie sich frei, dies so viel besser zu machen.

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(); 

Dies ist alles mit Stücken bereits im Play 2.0-Framework.

+0

Dies funktionierte für uns in Play 2.2, scheint aber in Play 2.3 gebrochen zu haben ... selbst nach der Änderung von 'play.libs.ws'. Schätzen Sie, warum. –

+0

@EricWilson können Sie genauer als "gebrochen" sein? – nafg

+0

Tut mir leid, ich arbeite nicht mehr mit Spielen und habe keinen Zugriff auf diesen Code. –

0

Es scheint, basierend auf Play-API-Dokumentation, dass es keine integrierte für mehrteilige POST-Körper gibt.

Jedoch kann es möglich sein, eigenen mehrteiliger Körper mit der Methode

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

mit einem Typ T Ihrer Wahl zu erstellen und die entsprechenden Beschreibbare und ContentTypeOf zu Typen.

Aber das würde implizieren, wie mehrteilige Körper mit HTTP arbeiten.

+0

Können Sie ein Beispiel teilen? – angelokh

+0

Eigentlich habe ich Ihnen die Scala-API gezeigt, und Sie scheinen Java zu benutzen, tut mir leid. In Java gibt es eine Post (InputStream) -Methode. Vielleicht können Sie den richtigen Inhalt dann in einem Eingabestream erstellen :) –

+0

@angelokh: Hier ist ein Beispiel: http://cdmckay.org/blog/2015/06/24/how-to-add-support-for-posting- multipart-forms-to-the-play-scala-ws-library/ – cdmckay

3

Die einzige Lösung für den Moment, ohne auf externe Bibliotheken angewiesen zu sein, scheint die Anforderung Multipart Formulardaten manuell zu erstellen. Dies ist ein Beispiel, wie es getan werden kann, mit 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 ein java.util.Map<String, String> alle Namen/Wert-Paare enthalten sein würden Sie als Formular-Parameter übergeben werden soll. randomString ist ein randomisierter Wert, um die Grenze von Anforderung zu Anforderung zu ändern. Das Hinzufügen von Binärdaten würde genauso funktionieren.

Diese http://www.htmlcodetutorial.com/forms/form_enctype.html ist ein guter Platz zum Verweisen auf die Spezifikationen.

0

Wie Romain Sertelon vorgeschlagen hat, können Sie ein Writeable schreiben, um diesen Fall zu behandeln. Hier ist ein schrieb ich:

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

} 

Hier ist, wie es zu benutzen:

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) 
+0

Wirklich gute Idee! Es kompiliert es jedoch nicht mehr. Meinst du "import com.ning.http.client.multipart ..."? Außerdem fügt 'stringParts ++ fileParts' ein StringPart zu einem Seq [FilePart] hinzu, was zu einem Seq [BasePart] führt. 'MultipartRequestEntity' benötigt eine util.List [Part] (kein Array [PartBase]). Wird es in der neuesten Play Version 2.4.6 für dich kompiliert? –

3

Arbeitsbeispiel für die Wiedergabe 2.3-Ansatz, auch hinzugefügt content beim Hochladen der Datei.

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

Ein Link zu einer Lösung ist willkommen, aber stellen Sie bitte sicher, dass Ihre Antwort ohne sie nützlich ist: [Fügen Sie Kontext um den Link hinzu] (// meta.stackexchange.com/a/8259), damit Ihre Mitbenutzer eine Idee haben, was es ist und warum es dort ist, zitieren Sie dann den relevantesten Teil der Seite, auf den Sie verlinken, falls die Zielseite nicht verfügbar ist. [Antworten, die kaum mehr als ein Link sind, können gelöscht werden.] (// stackoverflow.com/help/deleted-answers) –

+0

@ M.A.R. Ich habe den Link entfernt, stattdessen habe ich den Code hier nur geschrieben –

+0

Danke! Viel besser. –