3

How can I convert io.ktor.utils.io.ByteReadChannel into kotlinx.coroutines.flow.Flow<java.nio.ByteBuffer>?

I use Ktor with this routing:

        post("/upload") {
            val channel: ByteReadChannel = call.receiveChannel()
            val flow: Flow<ByteBuffer> = channel.asByteBufferFlow() // my custom extension method
            transaction.execute {
                testDao.saveFile(flow)
            }
            call.respond("OK")
        }

The DAO uses R2DBC and Blob like this:

    override suspend fun saveFile(input: Flow<ByteBuffer>) {
        val connection = requireR2DBCTransactionConnection()
        val publisher: Publisher<ByteBuffer> = input.asPublisher()
        val statement: Statement = connection.createStatement("insert into bindata (data) values ($1)")
        statement.bind(0, Blob.from(publisher))
        val count: Int = statement.execute().awaitFirst().rowsUpdated.awaitFirst()
        if (count != 1) {
            throw IllegalStateException()
        }
    }

I tried to write this extension method but I failed:

fun ByteReadChannel.asByteBufferFlow(): Flow<ByteBuffer> = object : AbstractFlow<ByteBuffer>() {

    override suspend fun collectSafely(collector: FlowCollector<ByteBuffer>) {
        /* I have no idea */
    }

}

My main problem is that I have not found any similar sample and both ByteBuffer and ByteReadChannel is new for me.

Zoltán
  • 61
  • 4
  • Don't try to extend `AbstractFlow`, use the `flow` builder: `flow { channel.consumeEachBufferRange { buf, _ -> emit(buf) } }` – Marko Topolnik Feb 28 '20 at 09:31
  • @MarkoTopolnik `ConsumeEachBufferVisitor` returns Boolean; I return true so it should read the whole stream, but in the database the byte array is empty :( – Zoltán Feb 29 '20 at 08:05
  • @MarkoTopolnik I read the code of `consumeEachBufferRange`: `var continueFlag = false; while (continueFlag) {...}` I think the initial value should be true. Or not? – Zoltán Mar 01 '20 at 11:31
  • I'm not following, what code do you refer to? – Marko Topolnik Mar 01 '20 at 17:25
  • @MarkoTopolnik the implementation of `consumeEachBufferRange` in package `io.ktor.utils.io` filename `ConsumeEach.kt` module `kotlin-io-jvm` version `1.3.0` Inside this function there is a while loop whose block never runs because the initial condition always false. So its result is an empty Flow. I think this is a bug in the Ktor project so I submitted a bugreport: https://github.com/ktorio/ktor/issues/1693 – Zoltán Mar 02 '20 at 12:39
  • Seems they accepted this code with no tests at all against it. – Marko Topolnik Mar 02 '20 at 13:57

0 Answers0