我使用的是Play 2.2.2并且尝试将测试添加到我的文件上传功能中失败了。我知道有很多关于这方面的问题,我已经检查了很多,但是要么他们不工作,要么API已被弃用且不起作用,或者失败并出现一些虚假错误。
这是浏览器中有效请求的样子(使用Firefox的WebDeveloper插件):
-----------------------------479326525221683770414613115
Content-Disposition: form-data; name="files[]"; filename="upload_aum_sample.csv"
Content-Type: text/csv
AccountName,AuM,Name
IX_CH1,10,A
IX_CH2,20,B
IX_CH3,30,C
IX_CH4,40,D
IX_CH5,50,E
IX_CH6,60,F
IX_CH7,70,G
IX_CH8,80,H
-----------------------------479326525221683770414613115--
这是我迄今为止没有成功的尝试:
编译并发出警告,即routeAndCall
已被弃用:
"upload file correctly" in new WithApplication {
val fileName = "upload_aum_sample.csv"
val file = getClass().getResource(fileName).getFile()
val data = MultipartFormData(Map(), List(FilePart("files[]", fileName, Some("text/csv"), file)), List(), List())
val result = routeAndCall(FakeRequest(POST, "/aum/upload/do", FakeHeaders(), data).withSession("username" -> "Test")).get
status(result) must equalTo(OK)
contentType(result) must beSome.which(_ == "application/json")
}
并导致异常:
[info] ! upload file correctly
[error] MatchError: <function1> (of class play.core.Router$Routes$$anon$4) (Helpers.scala:187)
[error] play.api.test.RouteInvokers$$anonfun$routeAndCall$1.apply(Helpers.scala:187)
[error] play.api.test.RouteInvokers$$anonfun$routeAndCall$1.apply(Helpers.scala:187)
[error] play.api.test.RouteInvokers$class.routeAndCall(Helpers.scala:187)
[error] AumUploadPageSpec.routeAndCall(AumUploadPageSpec.scala:30)
[error] play.api.test.RouteInvokers$class.routeAndCall(Helpers.scala:178)
[error] AumUploadPageSpec.routeAndCall(AumUploadPageSpec.scala:30)
[error] AumUploadPageSpec$$anonfun$12$$anon$3$delayedInit$body.apply(AumUploadPageSpec.scala:73)
[error] play.api.test.WithApplication$$anonfun$around$1.apply(Specs.scala:20)
[error] play.api.test.WithApplication$$anonfun$around$1.apply(Specs.scala:20)
[error] play.api.test.PlayRunners$class.running(Helpers.scala:45)
[error] play.api.test.Helpers$.running(Helpers.scala:364)
[error] play.api.test.WithApplication.around(Specs.scala:20)
[error] play.api.test.WithApplication.delayedInit(Specs.scala:17)
[error] AumUploadPageSpec$$anonfun$12$$anon$3.<init>(AumUploadPageSpec.scala:48)
[error] AumUploadPageSpec$$anonfun$12.apply(AumUploadPageSpec.scala:48)
[error] AumUploadPageSpec$$anonfun$12.apply(AumUploadPageSpec.scala:48)
编译罚款无警告
"upload file correctly" in new WithApplication {
val fileName = "upload_aum_sample.csv"
val file = getClass().getResource(fileName).getFile()
val data = MultipartFormData(Map(), List(FilePart("files[]", fileName, Some("text/csv"), file)), List(), List())
val result = controllers.Application.uploadDo("aum")(FakeRequest(POST, "/aum/upload/do", FakeHeaders(), data).withSession("username" -> "Test")).run
status(result) must equalTo(OK) // <<<<<<< test fails here
contentType(result) must beSome.which(_ == "application/json")
}
但是测试失败是由于服务器响应400而不是200而不是OK
[info] x upload file correctly
[error] '400' is not equal to '200' (AumUploadPageSpec.scala:53)
更新1:如果嵌入文件内容而不是文件,我仍会得到相同的错误,即 改变
val file = getClass().getResource(fileName).getFile()
到
val file = scala.io.Source.fromFile(getClass().getResource(fileName).getFile()).map(_.toByte).toArray
更新2:分别是路由和服务器端代码:
# Generic controllers
POST /:context/upload/do controllers.Application.uploadDo(context: String)
//------------------------------------------------------------------------
/**
* Action that uploads a file for a given context
* @param context the input context
*/
def uploadDo(context: String) = Action(parse.multipartFormData) { implicit request ⇒
request.body.file("files[]").map { file ⇒
val filename = file.filename
val contentType = file.contentType
}
Ok(Json.parse(
"""{"files": [
{
"name": "picture1.jpg",
"size": 902604,
"error": "Filetype not allowed"
},
{
"name": "picture2.jpg",
"size": 841946,
"error": "Filetype not allowed"
}
]}"""))
}
答案 0 :(得分:1)
this question的最佳答案(令人恼火地未被接受)解决了我的问题。为了完整起见,我将其包括在内。
trait FakeMultipartUpload {
case class WrappedFakeRequest[A](fr: FakeRequest[A]) {
def withMultipart(parts: (String, ContentBody)*) = {
// create a multipart form
val entity = new MultipartEntity()
parts.foreach { part =>
entity.addPart(part._1, part._2)
}
// serialize the form
val outputStream = new ByteArrayOutputStream
entity.writeTo(outputStream)
val bytes = outputStream.toByteArray
// inject the form into our request
val headerContentType = entity.getContentType.getValue
fr.withBody(bytes).withHeaders(CONTENT_TYPE -> headerContentType)
}
def withFileUpload(fileParam: String, file: File, contentType: String) = {
withMultipart(fileParam -> new FileBody(file, contentType))
}
}
implicit def toWrappedFakeRequest[A](fr: FakeRequest[A]) = WrappedFakeRequest(fr)
// override Play's equivalent Writeable so that the content-type header from the FakeRequest is used instead of application/octet-stream
implicit val wBytes: Writeable[Array[Byte]] = Writeable(identity, None)
}
@RunWith(classOf[JUnitRunner])
class AumUploadPageSpec extends PlaySpecification with FakeMultipartUpload {
//------------------------------------------------------------------------
"upload file correctly" in new WithApplication {
val fileName = "idxsrs_aum_2014-06-04.csv"
val uploadFile = new File(getClass().getResource(fileName).getPath())
val request = FakeRequest(POST, "/aum/upload/do").withFileUpload("files[]", uploadFile, "text/csv")
val response = route(request).get
status(response) must equalTo(OK)
contentType(response) must beSome.which(_ == "application/json")
}
}