4

http://www.playframework.org/documentation/2.0/ScalaStreamの「大量のデータの送信」の最初の例と同様のことを達成しようとしています。

私が別の方法で行っているのは、応答で連結したいファイルが複数あることです。次のようになります。

def index = Action {

  val file1 = new java.io.File("/tmp/fileToServe1.pdf")
  val fileContent1: Enumerator[Array[Byte]] = Enumerator.fromFile(file1)    

  val file2 = new java.io.File("/tmp/fileToServe2.pdf")
  val fileContent2: Enumerator[Array[Byte]] = Enumerator.fromFile(file2)   

  SimpleResult(
    header = ResponseHeader(200),
    body = fileContent1 andThen fileContent2
  )

}

何が起こるかというと、最初のファイルの内容だけが応答に含まれているということです。

ただし、以下のような少し単純なものでも問題なく動作します。

fileContent1 = Enumerator("blah" getBytes)
fileContent2 = Enumerator("more blah" getBytes)

SimpleResult(
  header = ResponseHeader(200),
  body = fileContent1 andThen fileContent2
)

何が間違っているのですか?

4

2 に答える 2

6

私はこれのほとんどをプレイコードを読んで得たので、誤解しているかもしれませんが、私が見ることができるものから:Enumerator.fromFilefunction(Iteratee.scala [ docs] [src])は列挙子を作成します。ファイルからの読み取りが完了したときのIterateeの出力。Iteratee[Byte, Byte]Input.EOF

プレイによると!リンクしたドキュメントの例では、列挙子の最後にInput.EOF(Enumerator.eofのエイリアス)を手動で追加することになっています。Input.EOFをファイルのバイト配列の最後に自動的に追加することが、返されるファイルが1つだけになる理由だと思います。

Play Consoleでの同等の簡単な例は、次のとおりです。

scala> val fileContent1 = Enumerator("blah") andThen Enumerator.eof
fileContent1: play.api.libs.iteratee.Enumerator[java.lang.String] = play.api.libs.iteratee.Enumerator$$anon$23@2256deba

scala> val fileContent2 = Enumerator(" and more blah") andThen Enumerator.eof
fileContent2: play.api.libs.iteratee.Enumerator[java.lang.String] = play.api.libs.iteratee.Enumerator$$anon$23@7ddeef8a

scala> val it: Iteratee[String, String] = Iteratee.consume[String]()
it: play.api.libs.iteratee.Iteratee[String,String] = play.api.libs.iteratee.Iteratee$$anon$18@6fc6ce97

scala> Iteratee.flatten((fileContent1 andThen fileContent2) |>> it).run.value.get
res9: String = blah

修正は、まだ試していませんが、数レベル深くして関数を直接使用し、連結するすべてのファイルが読み取られるまでsEnumerator.fromCallbackを返し続けるカスタムレトリバー関数を渡すことです。デフォルトとは何か、およびそれを変更する方法についてはArray[Byte]、関数の実装を参照してください。fromStream

これを行う方法の例(から適応Enumerator.fromStream):

def fromFiles(files: List[java.io.File], chunkSize: Int = 1024 * 8): Enumerator[Array[Byte]] = {
  val inputs = files.map { new java.io.FileInputStream(_) }
  var inputsLeftToRead = inputs
  Enumerator.fromCallback(() => {
    def promiseOfChunkReadFromFile(inputs: List[java.io.FileInputStream]): Promise[Option[Array[Byte]]] = {
      val buffer = new Array[Byte](chunkSize)
      (inputs.head.read(buffer), inputs.tail.headOption) match {
        case (-1, Some(_)) => {
          inputsLeftToRead = inputs.tail
          promiseOfChunkReadFromFile(inputsLeftToRead)
        }
        case (-1, None) => Promise.pure(None)
        case (read, _) => {
          val input = new Array[Byte](read)
          System.arraycopy(buffer, 0, input, 0, read)
          Promise.pure(Some(input))
        }
      }
    }
    promiseOfChunkReadFromFile(inputs)
  }, {() => inputs.foreach(_.close())})
}
于 2012-09-26T18:28:51.373 に答える
0

これは、一連のオーディオファイルを次々に一緒に再生するために使用しているPlayFramework2.2コードです。IteratorのJavaに変換されるEnumerationscalaの使用を見ることができますjava.io.SequenceInputStream

def get_files(name: String) = Action { implicit request =>
  import java.lang.IllegalArgumentException
  import java.io.FileNotFoundException
  import scala.collection.JavaConverters._
  try {
    val file1 = new java.io.File("songs/phone-incoming-call.ogg")
    val file2 = new java.io.File("songs/message.ogg")
    val file3 = new java.io.File("songs/desktop-login.ogg")
    val inputStream1 = new java.io.FileInputStream(file1)
    val inputStream2 = new java.io.FileInputStream(file2)
    val inputStream3 = new java.io.FileInputStream(file3)
    val it = Iterator(inputStream1, inputStream2, inputStream3)
    val seq = new java.io.SequenceInputStream(it.asJavaEnumeration)
    val fileContent: Enumerator[Array[Byte]] = Enumerator.fromStream(seq)
    val length: Long = file1.length + file2.length + file3.length
    SimpleResult(
      header = ResponseHeader(200, Map(CONTENT_LENGTH -> length.toString)),
      body = fileContent
    )
  }
  catch {
    case e: IllegalArgumentException =>
      BadRequest("Could not retrieve file. Error: " + e.getMessage)
    case e: FileNotFoundException =>
      BadRequest("Could not find file. Error: " + e.getMessage)
  }
}

私のコードの唯一の問題は(私が探しているものに関して)、それがストリームであるということです。つまり、を送信していても、前の時点から再生を再開することはできませんCONTENT_LENGTH。たぶん私は間違った方法で全長を計算しています。

それに関する他の問題は、オーディオファイルが同じタイプ(予想される)、同じ量のチャンネル、同じサンプルレート(理解するのに時間がかかりました)である必要があることです。たとえば、すべてがogg44100Hz2チャンネルである必要があります。

于 2014-01-02T17:06:26.863 に答える