为什么我没有在SimpleResult体中组合枚举器?

时间:2012-09-26 13:56:55

标签: scala playframework playframework-2.0

我试图在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
)

我错了什么?

2 个答案:

答案 0 :(得分:6)

我从阅读游戏代码中获得了大部分内容,所以我可能会误解,但我可以看到:Enumerator.fromFile函数(Iteratee.scala [docs] [src] )创建一个枚举器,当应用于Iteratee[Byte, Byte]时,将Input.EOF附加到Iteratee的输出,当它从文件中读取时。

根据Play!您链接到的docs示例应该在枚举器的末尾手动追加一个Input.EOF(Enumerator.eof的别名)。我认为,将Input.EOF自动附加到文件字节数组的末尾是您只返回一个文件的原因。

播放控制台中等效的简单例子是:

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

修复,虽然我还没有尝试过这个但是会更深入一些并直接使用Enumerator.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())})
}

答案 1 :(得分:0)

这是我用来一起播放一堆音频文件的播放框架2.2代码。您可以看到使用scala Iterator转换为Enumeration的{​​{1}}。

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) } } 我也无法从之前的某个时间点恢复播放。也许我是以错误的方式计算总长度。

它的另一个问题是音频文件需要是相同的类型(预期),相同数量的通道和相同的采样率(花了我一段时间来弄清楚),例如所有必须是ogg 44100Hz 2通道。