Playframework 2.0 播放2.0如何使用WS.url或WS.WSRequest发布MultipartFormData

Playframework 2.0 播放2.0如何使用WS.url或WS.WSRequest发布MultipartFormData,playframework-2.0,Playframework 2.0,在JavaHTTP请求中,我们可以这样做以使多部分Http POST HttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost(url); FileBody bin = new FileBody(new File(fileName)); StringBody comment = new StringBody("Filename: " + fileName); MultipartEntit

在JavaHTTP请求中,我们可以这样做以使多部分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();
如何使用WS.url或WS.WSRequest实现同样的功能

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

根据PlayAPI文档,似乎没有内置的多部分POST主体

但是,可以使用该方法创建自己的多部分主体

post[T](body: T)(implicit wrt: Writeable[T], ct: ContentTypeOf[T]): Future[Response]
使用您选择的T类型,以及相应的Writeable和ContentTypeOf类型


但这意味着要深入研究多部分实体如何与HTTP一起工作。

这太草率了,而且肯定可以清理,但下面是我为让它工作所做的。让这一切变得更好吧

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();
import java.io.ByteArrayInputStream;
导入java.io.ByteArrayOutputStream;
导入play.libs.WS;
导入com.ning.http.multipart.FilePart;
导入com.ning.http.multipart.MultipartRequestEntity;
导入com.ning.http.multipart.Part;
ByteArrayOutputStream bos=新建ByteArrayOutputStream();
//建立多部分
列表部件=新的ArrayList();
添加(新文件部分(“文件”,新文件(文件名));
零件[]零件SA=零件.toArray(新零件[parts.size()]);
//将其添加到MultipartRequestEntity
MultipartRequestEntity reqE=新的MultipartRequestEntity(partsA,null);
要求书面请求(bos);
InputStream Requis=新的ByteArrayInputStream(bos.toByteArray());
WS.WSRequestHolder req=WS.url(InterchangeConfig.conflateUrl+“数据集”)
.setContentType(reque.getContentType());
要求职位(要求职位)地图(…);
//或req.post(reqIS.get();

这都是使用Play 2.0框架中已有的片段。

目前唯一的解决方案,不依赖外部库,似乎是手动创建多部分表单数据请求。这是一个如何使用
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
将是一个
java.util.Map
,其中包含要作为表单参数传递的所有名称/值对
randomString
是一个随机化值,用于在不同请求之间更改边界。添加二进制数据也会以同样的方式工作


这是一个了解规范的好地方。

正如Romain Sertelon所建议的,您可以编写一个可写的程序来处理这种情况。这是我写的一封信:

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

}
以下是如何使用它:

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)

使用上述方法的Play2.3的工作示例,在上传文件时也添加了contentType

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;
    }
}
公共承诺上载(Http.MultipartFormData.FilePart-policyFilePart,String-contentType){ ByteArrayOutputStream bos=新建ByteArrayOutputStream(); 列表部件=新的ArrayList(); 试一试{ 添加(新文件部分(“文件”,policyFilePart.getFile(),contentType,null)); 增加(新的StringPart(“参数1”,“值1”)); 添加(新的StringPart(“参数2”,“值2”)); 零件[]零件SA=零件.toArray(新零件[parts.size()]); //将其添加到多部分请求实体 MultipartRequestEntity requestEntity=新的MultipartRequestEntity(partsA,新的FluentCaseInsensitiveStringsMap()); requestEntity.writeRequest(bos); InputStream Requis=新的ByteArrayInputStream(bos.toByteArray()); 返回WS.url(baseUrl+“上传”) .setContentType(requestEntity.getContentType()) .post(requis).map(新函数(){ @凌驾 公共WSResponse应用(WSResponse WSResponse)抛出可丢弃{ 返回wsResponse; } }); }捕获(IOE异常){ e、 printStackTrace(); 返回null; } }
接受的答案与播放2.5不兼容。文档中的答案也不适用于2.5。
以下方法效果良好:

Http.MultipartFormData.FilePart part = new Http.MultipartFormData.FilePart("fileKey",
                "abc.zip", "multipart/form-data",
                FileIO.fromFile(new File("/home/testData/abc.zip")));
List<Http.MultipartFormData.Part<Source<ByteString, ?>>> data = Arrays.asList(part);
Http.RequestBuilder requestBuilder = AuthFakeRequest.getAuthFakeRequest(routes.MyController.uploadZip()).method(POST)
                .bodyMultipart(data, mat);
Result result = route(app, requestBuilder);
Http.MultipartFormData.FilePart=new Http.MultipartFormData.FilePart(“fileKey”,
“abc.zip”、“多部分/表单数据”,
FileIO.fromFile(新文件(“/home/testData/abc.zip”);
列表数据=数组.asList(部分);
Http.RequestBuilder RequestBuilder=AuthFakeRequest.getAuthFakeRequest(routes.MyController.uploadZip()).method(POST)
.bodyMultipart(数据、mat);
结果=路线(应用程序、requestBuilder);

对于
mat
app
对象,它们是在继承
play.test.WithApplication
类时获得的。

您可以分享一个示例吗?事实上,我已经向您展示了Scala API,您似乎在使用Java,对不起。在Java中,有一个post(InputStream)方法。也许你可以在输入流中创建正确的内容:)@angelokh:这里有一个例子:这在Play 2.2中对我们有效,但在Play 2.3中似乎已经失效。。。即使在修复了
play.libs.ws
更改之后。非常感谢您的猜测。@EricWilson您能比“断开”更具体一点吗?对不起,我不再使用play,也没有访问该代码的权限。查看MultipartRequestEntity的源代码,如果您为Headers传递null,它看起来会爆炸。类似的东西在scala中对我适用。对于第二个ctor参数,我编写了新的FluentCaseInsensitiveStringsMap,我发布了如下内容:
.post(baos.toByteArray())(Writeable.wBytes,ContentTypeOf(Some(mpre.getContentType))
真是个好主意!不过,它不再编译。您的意思是导入com.ning.http.client.multipart…?另外,
stringParts++fileParts
将StringPart添加到Seq[FilePart],从而生成Seq[BasePart]
MultipartRequestEntity
需要util.List[Part](而不是数组[PartBase])。它是否在最新的Play release 2.4.6中为您编译?欢迎提供指向解决方案的链接,但请确保您的答案在没有它的情况下是有用的:这样您的其他用户就会知道它是什么以及为什么存在,然后引用您链接到的页面的最相关部分,以防目标页面不可用@M.A.R.我已经删除了链接,而是在这里编写了代码only@live_alone我试图在游戏2.4中使用这个,但是由于