I have most of this from reading the game code, so I may have misunderstood, but from what I see: the Enumerator.fromFile function (Iteratee.scala [docs] [src] ) creates an enumerator that when applied to Iteratee[Byte, Byte] adds Input.EOF to the output of Iteratee when it reads from a file.
According to Play! The docs you are associated with, you must manually add Input.EOF (alias Enumerator.eof) at the end of the enumerator. I would think that with the addition of Input.EOF, automatically added to the end of the file byte array, it is the reason that you only get one returned file.
Another simpler example in the playback console would be:
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
The fix, although I have not tried this yet, would go a few levels deeper and use the Enumerator.fromCallback function directly and pass it a custom retriever function that will return Array[Byte] until all the files you want to concatenate have been read. Refer to the implementation of the fromStream function to find out what the default is and how to change it.
An example of how to do this (adapted from 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())}) }