Converting from Enumerator to Sink when using java OutputStream

197 views
Skip to first unread message

Dragisa Krsmanovic

unread,
Jun 23, 2016, 12:45:15 PM6/23/16
to play-framework
I have a controller that returns chunked output and needs an OtputStream.
Implementation using Enumerators works fine:

    val enumerator = Enumerator.outputStream(os => report.write(os))
    Ok.chunked(enumerator)


Since chunked with Enumerator is now deprecated, I am trying to convert it to Akka streams. I have something like this:

    val source = StreamConverters.asOutputStream().mapMaterializedValue(os => report.write(os))
    Ok.chunked(source)



Unfortunately this is not working. Requests just time out.
I can see that report.write(os) gets called, but no output gets written to Http request.

What is the correct way to convert Enumerator.outputStream(...) to Akka streams ?

Thanks,
Dragisa

Nick Howes

unread,
Jun 23, 2016, 2:48:04 PM6/23/16
to play-framework
Does report.write(os) write the whole output? Have you tried calling os.close() afterwards (inside mapMaterializedValue)? It may be waiting for the output stream to be closed or flushed.

Dragisa Krsmanovic

unread,
Jun 24, 2016, 1:07:00 PM6/24/16
to play-framework
Yes, report.write() writes whole output.

FYI, it's Apache POI. I am writing an Excel spreadsheet in chunks to avoid rendering it all in memory.

I tried closing the output stream too

    val source = StreamConverters
      .asOutputStream()
      .mapMaterializedValue { os =>
        try {
          log.debug("Writing to os")
          report.write(os)
        } finally {
          log.debug("Closing os")
          os.close()
        }
      }


It never enters the finally block.

The example with Enumerator works fine.

Christian Schmitt

unread,
Jun 24, 2016, 5:10:07 PM6/24/16
to play-framework
Just for reference could you try something like that:
val inputStream = new PipedInputStream()
val outputStream = new PipedOutputStream(ios)
Future{ /** Write to outputStream */ }
Ok.chunked(StreamConverters.fromInputStream(() => inputStream))

Dragisa Krsmanovic

unread,
Jun 24, 2016, 5:21:56 PM6/24/16
to play-fr...@googlegroups.com
Christian,

That does seem to work. Thank you !

I wonder, what is the proper use of StreamConverters.asOutputStream() ?

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/b53aa337-96f9-4876-9071-9be6d3beea7c%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--

Dragiša Krsmanović | Platform Engineer | Ticketfly

drag...@ticketfly.com

@ticketfly | ticketfly.com/blog | facebook.com/ticketfly

Message has been deleted

Dragisa Krsmanovic

unread,
Jun 27, 2016, 12:38:27 PM6/27/16
to play-fr...@googlegroups.com
Interestingly,

val source = StreamConverters.asOutputStream().mapMaterializedValue(os => Future{report.write(os)})
Ok.chunked(source)

Doesn't seem to work. The request times out. However, it's different from when you don't have a Future. It starts to upload chunks but never finishes. (Yes, I tried putting try-finally around report.write to close output stream.)


On Fri, Jun 24, 2016 at 3:13 PM, Christian Schmitt <c.sc...@briefdomain.de> wrote:
Actually I guess the following could work, too:

val source = StreamConverters.asOutputStream().mapMaterializedValue(os => Future{report.write(os)})
Ok.chunked(source)


Btw. it's best to close the outputStream after write, so that the source will be closed and the upstream handler knows that data writing is finished.

Actually I guess that works, too:
val (outputStream, publisher) = StreamConverters.asOutputStream().toMat(Sink.asPublisher(false))(Keep.both).run()
Future { /* write to outputstream */ }
Ok.chunked(Source.fromPublisher(publisher))

For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages