我目前正在使用以下内容与Play2 / Scala一起使用FileUploader Javascript实用程序将文件上传到我的服务器:
def fileUploader = Action(parse.multipartFormData) { request =>
request.body.file("qqfile").map { picture =>
import java.io.File
val filename = picture.filename
val contentType = picture.contentType
picture.ref.moveTo(new File("/tmp",filename))
Ok(Json.toJson(Map( "success" -> "true" )))
}.getOrElse {
Ok(Json.toJson(Map( "error" -> "error occured")))
}
}
我只处理小文件(< 10MB),我想使用casbah使用Mongo驱动程序将这些文件直接写入Mongo Document或GridFS。我知道我可以从磁盘读取保存的文件,但有没有办法从内存中处理这一切而不先在磁盘上缓冲文件?
这里的播放文档建议编写一个自定义BodyParser(http://www.playframework.com/documentation/2.1.0/ScalaFileUpload),但似乎没有关于如何编写一个的文档。目前还不清楚Scaladocs的API /实现是如何工作的。我尝试查找MultiPartFormData源代码以查看它是如何工作的,但我似乎无法在他们的Git仓库中找到它:
https://github.com/playframework/Play20/tree/master/framework/src/play/src/main/scala/play/api/mvc
我搜索了很多,但似乎无法找到一个很好的例子。
答案 0 :(得分:13)
未测试
Multipart
的{{1}}对象为我们做了很多工作。我们需要做的第一件事是为BodyParsers
编写一个处理程序。我假设您希望文件部分为FilePart
。
Array[Byte]
下一步是定义你的身体解析器:
def handleFilePartAsByteArray: PartHandler[FilePart[Array[Byte]]] =
handleFilePart {
case FileInfo(partName, filename, contentType) =>
// simply write the data to the a ByteArrayOutputStream
Iteratee.fold[Array[Byte], ByteArrayOutputStream](
new ByteArrayOutputStream()) { (os, data) =>
os.write(data)
os
}.mapDone { os =>
os.close()
os.toByteArray
}
}
然后,为了使用它,请在def multipartFormDataAsBytes:BodyParser[MultipartFormData[Array[Byte]]] =
multipartFormData(handleFilePartAsByteArray)
指定它:
Action
上面的代码中的一些类型和方法有点难以找到。以下是您需要的完整导入列表:
def fileUploader = Action(multipartFormDataAsBytes) { request =>
request.body.files foreach {
case FilePart(key, filename, contentType, bytes) => // do something
}
Ok("done")
}
答案 1 :(得分:2)
自发布以来,Play API的更改金额就不错。我有一个类似的用例,其中我不需要临时文件,并将上面的内容转换为以下内容,这似乎可以与Play 2.6配合使用,以防有人需要:
def byteStringFilePartHandler: FilePartHandler[ByteString] = {
case FileInfo(partName, filename, contentType) =>
Accumulator(Sink.fold[ByteString, ByteString](ByteString()) { (accumulator, data) =>
accumulator ++ data
}.mapMaterializedValue(fbs => fbs.map(bs => {
FilePart(partName, filename, contentType, bs)
})))
}
def multipartFormDataAsBytes: BodyParser[MultipartFormData[ByteString]] =
playBodyParsers.multipartFormData(byteStringFilePartHandler)
在控制器中使用它,请确保您注入了PlayBodyParsers
并提供了ExecutionContext
,并在下面导入了这些内容:
import akka.stream.scaladsl.Sink
import akka.util.ByteString
import javax.inject._
import play.api.libs.streams.Accumulator
import play.api.mvc.MultipartFormData.FilePart
import play.api.mvc._
import play.core.parsers.Multipart.{FileInfo, FilePartHandler}
import scala.concurrent.ExecutionContext
@Singleton
class HomeController @Inject()(cc: ControllerComponents, playBodyParsers: PlayBodyParsers)
(implicit ec: ExecutionContext) extends AbstractController(cc) {
def index = Action(multipartFormDataAsBytes) { request =>
request.body.file("image").foreach((image) => {
val arr = image.ref.toByteBuffer.array()
println(arr)
})
Ok("got bytes!")
}
}
答案 2 :(得分:1)
根据 Matt 的回答(嗨,Matt!),我最终需要为 Play 2.8 稍微调整一下(我怀疑 API 进一步发展了):
def byteStringFilePartHandler: FilePartHandler[ByteString] = {
case FileInfo(partName, filename, contentType, dispositionType) =>
Accumulator(Sink.fold[ByteString, ByteString](ByteString()) { (accumulator, data) =>
accumulator ++ data
}.mapMaterializedValue(fbs => fbs.map(bs => {
FilePart(partName, filename, contentType, bs)
})))
}
def multipartFormDataAsBytes: BodyParser[MultipartFormData[ByteString]] =
controllerComponents.parsers.multipartFormData(byteStringFilePartHandler)
因为我的用例是上传文本文件,所以我从结果 request
中提取它:
val body: String = request.body.files.head.ref.utf8String
(为了安全起见,不那么快速和肮脏的代码会在那里使用 headOption
。)