Search code examples
scalaplayframeworkplayframework-2.2loops

File writing iteratee does not receive EOF for WS.get


I have created a simple iteratee to download a file using WS as explained in this link.

Consider the following snippet:

import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import org.specs2.mutable.Specification
import org.specs2.time.NoTimeConversions
import play.api.libs.Files.TemporaryFile
import play.api.libs.iteratee.{Done, Input, Cont, Iteratee}
import play.api.libs.ws.WS
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

class DummySpec extends Specification with NoTimeConversions {
  def fileChannelIteratee(channel: FileChannel): Iteratee[Array[Byte], Unit] = Cont {
    case Input.EOF =>
      println("Input.EOF")
      channel.close()
      Done(Unit, Input.EOF)
    case Input.El(bytes) =>
      println("Input.El")
      val buf = ByteBuffer.wrap(bytes)
      channel.write(buf)
      fileChannelIteratee(channel)
    case Input.Empty =>
      println("Input.Empty")
      fileChannelIteratee(channel)
  }

  "fileChannelIteratee" should {
    "work" in {
      val file = TemporaryFile.apply("test").file
      val channel = FileChannel.open(file.toPath)

      val future = WS.url("http://www.example.com").get(_ => fileChannelIteratee(channel)).map(_.run)

      Await.result(future, 10.seconds)

      file.length !== 0
    }
  }
}

Calling .map(_.run) after WS.get seems to have no effect here as the iteratee does not seem to receive Input.EOF. It prevents me from being able to close the channel. This is the output I get:

Input.El
[info] DummySpec
[info]
[info] fileChannelIteratee should
[info] x work (941 ms)
[error]    '0' is equal to '0' (DummySpec.scala:37)
[info]
[info]
[info] Total for specification DummySpec
[info] Finished in 948 ms
[info] 1 example, 1 failure, 0 error

What am I doing wrong?

I am using Play Framework 2.2.2.

Thanks in advance.


Solution

  • I was opening the FileChannel in a wrong way. It seems to default to read mode according to this link when no parameters are given.

    The exception thrown from channel.write was being swallowed by map operation as the return type of the whole operation is Future[Future[Unit]]. The outer Future is in a successful state even if the internal one fails in this case. flatMap should be used instead.