Why can't I combine counters in the body of SimpleResult?

I am trying to achieve something similar to the first example in the section "Sending large amounts of data" at http://www.playframework.org/documentation/2.0/ScalaStream .

What I do differently is that I have several files that I want to merge into a response. It looks like this:

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 ) 

}

What happens is that the response contains only the contents of the first file.

Something a little simpler, as shown below, works great:

 fileContent1 = Enumerator("blah" getBytes) fileContent2 = Enumerator("more blah" getBytes) SimpleResult( header = ResponseHeader(200), body = fileContent1 andThen fileContent2 ) 

How am I wrong?

+4
source share
2 answers

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())}) } 
+6
source

This is the 2.2 program code for the game that I use to combine a bunch of audio files one by one. You can see the use of scala Iterator , which translates to java Enumeration for 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) } } 

The only problem with my code (in terms of what I'm looking for) is that it is a thread, which means that I cannot resume the game from the previous moment, even if I send CONTENT_LENGTH . Perhaps I am not calculating the total length correctly.

Another problem is that the audio files must be of the same type (expected), the same number of channels and the same sampling frequency (it took me some time to understand), for example, all should have ogg 44100Hz 2 channels.

0
source

Source: https://habr.com/ru/post/1436380/


All Articles