在 play2 2.4.6 中发送多部分表单数据时超时

timeout when sending multipart form data in play2 2.4.6

我正在尝试玩 2.4.6,但我正在努力更新我的 multipart/form 数据 posting。 "old" 使用的代码:

import com.ning.http.multipart._ libs

而且非常直接:

val parts = Array[Part](
   new FilePart("file", currentFile)
)

val mpre = new MultipartRequestEntity(parts, new FluentCaseInsensitiveStringsMap)
val baos = new ByteArrayOutputStream
mpre.writeRequest(baos)
val bytes = baos.toByteArray
val contentType = mpre.getContentType     
// Now just send the data to the WS API
 WS.url(url)
    .post(bytes)(Writeable.wBytes, ContentTypeOf(Some(contentType))).map { response =>
    Logger.info("" + response.status)
//  Cool code here to handle stuff
}

现在似乎 MultipartRequestEntity 和所有好的东西都不见了..所以我试着像这样重写它(抱歉丑陋的代码 :)):

def uploadFileForOrder(id: Long) = Action {
    implicit request =>
    import com.ning.http.client.AsyncHttpClient
    import com.ning.http.client.multipart.FilePart
    import com.ning.http.client.multipart.StringPart
    import java.io.File
    import play.api.libs.ws._

  val asyncHttpClient: AsyncHttpClient = WS.client.underlying
  val postBuilder = asyncHttpClient.preparePost(url)
  val builder = postBuilder
  .addBodyPart(new FilePart("file.pdf", new  
  File("/Path/to/file/file.pdf")))
  .addHeader("Expect", "100-continue")

  val response = asyncHttpClient.executeRequest(builder.build()).get();
  Ok(""+response.getResponseBody())
 }

但是这里发生的是请求在默认秒数后超时。 我当然尝试卷曲到 api 并且效果很好:

curl --form file=@Path/to/file/file.pdf 'https://url'    

我可能错过了一些非常微不足道的事情,但是什么?

超时堆栈跟踪。

    [debug] - play.api.libs.ws.ning.NingAsyncHttpClientConfigBuilder - buildHostnameVerifier: enabling hostname verification using class play.api.libs.ws.ssl.DefaultHostnameVerifier
[debug] - org.jboss.netty.channel.DefaultChannelFuture - The dead lock checker in DefaultChannelFuture has been disabled as requested at your own risk.
[info] - application - parts[FilePart name=deevi-sthlm_1424182188313.pdf contentType=application/octet-stream charset=null tranferEncoding=binary contentId=null dispositionType=null filename=deevi-sthlm_1424182188313.pdf]
[debug] - org.jboss.netty.handler.ssl.SslHandler - [id: 0xe4b02f13, /192.168.1.197:50403 => cool.api.com/212.16.165.50:443] HANDSHAKEN: TLS_RSA_WITH_AES_128_CBC_SHA
[debug] - com.ning.http.client.providers.netty.request.NettyConnectListener - Request using non cached Channel '[id: 0xe4b02f13, /192.168.1.197:50403 => cool.api.com/212.16.165.50:443]':
DefaultHttpRequest(chunked: false)
POST /api/v2/7519/resources/upload/internal/order/23/?token=aaaaaaaa HTTP/1.1
Content-Length: 919937
Content-Type: multipart/form-data; boundary=pKuhU4cHh8J6RhA1tqOEeujTjvje06TC8AW8ZvZD
Connection: keep-alive
Host: cool.api.com
Accept: */*
User-Agent: AHC/1.0

[debug] - com.ning.http.client.providers.netty.channel.pool.DefaultChannelPool - Closed 0 connections out of 0 in 0ms
[debug] - com.ning.http.client.providers.netty.channel.pool.DefaultChannelPool - Closed 0 connections out of 0 in 0ms
[debug] - com.ning.http.client.providers.netty.request.timeout.TimeoutTimerTask - Request timed out to cool.api.com/212.16.165.50:443 of 120000 ms for NettyResponseFuture{currentRetry=0,
    isDone=false,
    isCancelled=false,
    asyncHandler=com.ning.http.client.AsyncCompletionHandlerBase@293e6e7c,
    nettyRequest=com.ning.http.client.providers.netty.request.NettyRequest@5b564b51,
    content=null,
    uri=https://cool.api.com/api/v2/7519/resources/upload/internal/order/23/?token=aaaaa,
    keepAlive=true,
    httpHeaders=null,
    exEx=null,
    redirectCount=0,
    timeoutsHolder=com.ning.http.client.providers.netty.request.timeout.TimeoutsHolder@51f45b89,
    inAuth=false,
    statusReceived=false,
    touch=845942276} after 120333 ms
[debug] - com.ning.http.client.providers.netty.channel.ChannelManager - Closing Channel [id: 0xe4b02f13, /192.168.1.197:50403 => cool.api.com/212.16.165.50:443] 
[debug] - com.ning.http.client.providers.netty.request.NettyRequestSender - Aborting Future NettyResponseFuture{currentRetry=0,
    isDone=false,
    isCancelled=false,
    asyncHandler=com.ning.http.client.AsyncCompletionHandlerBase@293e6e7c,
    nettyRequest=com.ning.http.client.providers.netty.request.NettyRequest@5b564b51,
    content=null,
    uri=https://cool.api.com/api/v2/7519/resources/upload/internal/order/23/?token=214141414,
    keepAlive=true,
    httpHeaders=null,
    exEx=null,
    redirectCount=0,
    timeoutsHolder=com.ning.http.client.providers.netty.request.timeout.TimeoutsHolder@51f45b89,
    inAuth=false,
    statusReceived=false,
    touch=845942276}

[debug] - com.ning.http.client.providers.netty.handler.Processor - Channel Closed: [id: 0xe4b02f13, /192.168.1.197:50403 :> cool.api.com/212.16.165.50:443] with attribute INSTANCE
[debug] - com.ning.http.client.providers.netty.request.NettyRequestSender - Request timed out to cool.api.com/212.16.165.50:443 of 120000 ms
java.util.concurrent.TimeoutException: Request timed out to cool.api.com/212.16.165.50:443 of 120000 ms
    at com.ning.http.client.providers.netty.request.timeout.TimeoutTimerTask.expire(TimeoutTimerTask.java:47) [async-http-client-1.9.21.jar:na]
    at com.ning.http.client.providers.netty.request.timeout.RequestTimeoutTimerTask.run(RequestTimeoutTimerTask.java:48) [async-http-client-1.9.21.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:556) [netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:632) [netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:369) [netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) [netty-3.10.4.Final.jar:na]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45]
[debug] - com.ning.http.client.AsyncCompletionHandlerBase - Request timed out to cool.api.com/212.16.165.50:443 of 120000 ms
java.util.concurrent.TimeoutException: Request timed out to cool.api.com/212.16.165.50:443 of 120000 ms
    at com.ning.http.client.providers.netty.request.timeout.TimeoutTimerTask.expire(TimeoutTimerTask.java:47) [async-http-client-1.9.21.jar:na]
    at com.ning.http.client.providers.netty.request.timeout.RequestTimeoutTimerTask.run(RequestTimeoutTimerTask.java:48) [async-http-client-1.9.21.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:556) [netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:632) [netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:369) [netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) [netty-3.10.4.Final.jar:na]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45]

play.api.http.HttpErrorHandlerExceptions$$anon: Execution exception[[TimeoutException: Request timed out to cool.api.com/212.16.165.50:443 of 120000 ms]]
    at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:265) ~[play_2.11-2.4.6.jar:2.4.6]
    at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:191) ~[play_2.11-2.4.6.jar:2.4.6]
    at play.api.GlobalSettings$class.onError(GlobalSettings.scala:179) [play_2.11-2.4.6.jar:2.4.6]
    at play.api.DefaultGlobal$.onError(GlobalSettings.scala:212) [play_2.11-2.4.6.jar:2.4.6]
    at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:94) [play_2.11-2.4.6.jar:2.4.6]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$$anonfun$apply.applyOrElse(PlayDefaultUpstreamHandler.scala:151) [play-netty-server_2.11-2.4.6.jar:2.4.6]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$$anonfun$apply.applyOrElse(PlayDefaultUpstreamHandler.scala:148) [play-netty-server_2.11-2.4.6.jar:2.4.6]
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36) [scala-library-2.11.7.jar:na]
    at scala.util.Failure$$anonfun$recover.apply(Try.scala:216) [scala-library-2.11.7.jar:na]
    at scala.util.Try$.apply(Try.scala:192) [scala-library-2.11.7.jar:na]
    at scala.util.Failure.recover(Try.scala:216) [scala-library-2.11.7.jar:na]
    at scala.concurrent.Future$$anonfun$recover.apply(Future.scala:324) [scala-library-2.11.7.jar:na]
    at scala.concurrent.Future$$anonfun$recover.apply(Future.scala:324) [scala-library-2.11.7.jar:na]
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32) [scala-library-2.11.7.jar:na]
    at play.api.libs.iteratee.Execution$trampoline$.executeScheduled(Execution.scala:109) [play-iteratees_2.11-2.4.6.jar:2.4.6]
    at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:71) [play-iteratees_2.11-2.4.6.jar:2.4.6]
    at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:40) [scala-library-2.11.7.jar:na]
    at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:248) [scala-library-2.11.7.jar:na]
    at scala.concurrent.Promise$class.complete(Promise.scala:55) [scala-library-2.11.7.jar:na]
    at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:153) [scala-library-2.11.7.jar:na]
    at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:23) [scala-library-2.11.7.jar:na]
    at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40) [akka-actor_2.11-2.3.13.jar:na]
    at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397) [akka-actor_2.11-2.3.13.jar:na]
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) [scala-library-2.11.7.jar:na]
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) [scala-library-2.11.7.jar:na]
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.7.jar:na]
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.7.jar:na]
Caused by: java.util.concurrent.TimeoutException: Request timed out to cool.api.com/212.16.165.50:443 of 120000 ms
    at com.ning.http.client.providers.netty.request.timeout.TimeoutTimerTask.expire(TimeoutTimerTask.java:47) ~[async-http-client-1.9.21.jar:na]
    at com.ning.http.client.providers.netty.request.timeout.RequestTimeoutTimerTask.run(RequestTimeoutTimerTask.java:48) ~[async-http-client-1.9.21.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:556) ~[netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:632) ~[netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:369) ~[netty-3.10.4.Final.jar:na]
    at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) ~[netty-3.10.4.Final.jar:na]
    at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_45]
[debug] - play.core.netty.utils.ServerCookieDecoder - Skipping cookie because value '86030c54cfc1208b3a87ab3e0b3e3dd565940e8d0u07y2ap7kywplw*dqy.l*5uzexhfcv0_4nkev1w~p3cnk!(7d*4.)k_h(tbx3*v' contains invalid char '!'
[debug] - com.ning.http.client.providers.netty.channel.pool.DefaultChannelPool - Closed 0 connections out of 0 in 0ms

我还尝试 post 与:

.addHeader("Expect", "100-continue")
.addHeader("Content-Type","application/x-www-form-urlencoded")

但收到 413 请求实体太大。

Request DefaultHttpRequest(chunked: false)
POST /api/v2/7519/resources/upload/internal/order/23/?token=aaaaaaa HTTP/1.1
Expect: 100-continue
Content-Length: 919931
Content-Type: application/x-www-form-urlencoded; boundary=pI11GyXoLyybJYI3pR9iMZDgu8D1ZaH_Y4Qmc
Connection: keep-alive
Host: power.upsales.com
Accept: */*
User-Agent: AHC/1.0

Response DefaultHttpResponse(chunked: false)
HTTP/1.1 413 Request Entity Too Large
Server: nginx/1.4.4
Date: Thu, 11 Feb 2016 08:12:50 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1206
Connection: keep-alive
X-Content-Type-Options: nosniff

您可能正在点击 4 个月前修复并在 AsyncHttpClient 1.9.32 中发布的 this issue

默认情况下,Play2 2.4.6 ships with AsyncHttpClient 1.9.21 :(

升级。

Stephane 你之前的回答(你删除的那个 :)))让我觉得,也许服务器正在期待一些东西,但我错过了它。所以我潜入 curl 请求和 scala 请求代码和解决方案很简单。添加文件部分时,第一个参数应该是 "file" 否则服务器不会处理文件。

.addBodyPart(new FilePart("file", new File("/Path/to/file/file.pdf")))

所以代码看起来像这样。我还实现了一个异步版本,使其更像 Play2:

def uploadFileForOrder(id: Long) = Action.async {
implicit request =>

    import com.ning.http.client._
    import com.ning.http.client.{Response => AHCResponse}
    import com.ning.http.client.AsyncHttpClient
    import com.ning.http.client.AsyncCompletionHandler
    import com.ning.http.client.multipart.FilePart
    import com.ning.http.client.multipart.StringPart
    import java.io.File
    import play.api.libs.ws._
    import play.api.libs.ws.ning.NingWSResponse
    import scala.concurrent.{Future, Promise}


    val asyncHttpClient: AsyncHttpClient = WS.client.underlying
    val postBuilder =
      asyncHttpClient.preparePost("https://cool.api.com/api/resources/upload/internal/order/23/?token=asdasdasdasda")
      val builder = postBuilder
      .addBodyPart(new FilePart("file", new File("/Path/to/file/file.pdf")))
      .addHeader("Expect", "100-continue")

      val request = builder.build()
      val result = Promise[NingWSResponse]()
      asyncHttpClient.executeRequest(request, new AsyncCompletionHandler[AHCResponse]() {
        override def onCompleted(response: AHCResponse) = {
          result.success(play.api.libs.ws.ning.NingWSResponse(response))
          response
        }
        override def onThrowable(t: Throwable) {
          result.failure(t)
        }
      })
      val postFuture = result.future
      postFuture.map { response =>
        Ok("status"+response.statusText)
      }.recover {
        case e: Throwable => {
          BadRequest("error" + e)
        }
      }
    }