10

私は現在、FileUploaderJavascriptユーティリティを使用してPlay2/ Scalaで次のものを使用して、サーバーにファイルをアップロードしています。

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ドキュメントまたはGridFSに直接書き込みたいと考えています。保存したファイルをディスクから読み取ることができたのですが、最初にディスク上のファイルをバッファリングせずに、これをすべてメモリから処理する方法はありますか?

ここにあるPlayのドキュメントでは、カスタムの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

かなり検索しましたが、良い例が見つからないようです。

4

3 に答える 3

13

未テストMultipartオブジェクトは、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")
}

上記のコードの一部のタイプとメソッドは、見つけるのが少し難しいです。必要な場合のインポートの完全なリストは次のとおりです。

import play.api.mvc.BodyParsers.parse.Multipart.PartHandler
import play.api.mvc.BodyParsers.parse.Multipart.handleFilePart
import play.api.mvc.BodyParsers.parse.Multipart.FileInfo
import play.api.mvc.BodyParsers.parse.multipartFormData
import play.api.mvc.MultipartFormData.FilePart
import play.api.libs.iteratee.Iteratee
import java.io.ByteArrayOutputStream
import play.api.mvc.BodyParser
import play.api.mvc.MultipartFormData
于 2013-02-23T12:07:31.600 に答える
4

これが投稿されてから、PlayAPIはかなりの量を変更しました。一時ファイルが不要で、上記を次のように変換した同様のユースケースがありました。これは、誰かがこれを必要とする場合にPlay2.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!")
  }
}
于 2018-08-08T20:56:43.050 に答える
2

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そこで使用されません。)

于 2021-01-10T18:05:57.713 に答える