Ktor 3.5.0 Help

I/O Interoperability

Ktor supports non-blocking, asynchronous I/O built on top of kotlinx-io, a multiplatform Kotlin library providing basic I/O primitives. This allows you to stream HTTP request and response bodies, read or write files, and process data incrementally without loading it all into memory.

If you're working with external libraries or platforms that use different I/O models, you can use a set of adapters to interoperate with:

  • kotlinx-io types such as RawSource and RawSink

  • Java I/O types such as OutputStream

This page describes how to convert between Ktor’s I/O primitives (ByteReadChannel, ByteWriteChannel) and these external types.

Convert ByteReadChannel to RawSource

To convert a ByteReadChannel into a RawSource, use the .asSource() extension function:

client.prepareGet("https://httpbin.org/bytes/1024").execute { httpResponse -> val channel: ByteReadChannel = httpResponse.body() val source: RawSource = channel.asSource() val buffered = source.buffered() val firstByte = buffered.readByte() println("Read first byte from RawSource: $firstByte") }

Convert ByteWriteChannel to RawSink

To convert a suspending ByteWriteChannel into a RawSink, use the .asSink() extension function:

get("/sink") { call.respondBytesWriter { val sink: RawSink = this.asSink() sink.buffered().use { buffered -> buffered.writeString("Hello from kotlinx-io Sink!") } } }

The RawSink produced by this adapter uses runBlocking internally when flushing data, so flush operations may block the calling thread.

Convert RawSink to ByteWriteChannel

To wrap a RawSink as a suspending ByteWriteChannel, use the .asByteWriteChannel() extension function:

get("/raw-sink") { val buffer = Buffer() val channel = buffer.asByteWriteChannel() channel.writeByte(42) channel.writeFully("Hello via RawSink".toByteArray()) channel.flushAndClose() call.respondBytes(buffer.readByteArray()) }

This enables asynchronous writing to sinks from suspending functions. The returned channel is buffered. Use .flush() or .flushAndClose() to ensure that all data is written.

Convert OutputStream to ByteWriteChannel

To convert a Java OutputStream to a ByteWriteChannel, use the .asByteWriteChannel() extension function:

get("/output-stream") { val out = ByteArrayOutputStream() val channel = out.asByteWriteChannel() channel.writeFully("Hello from OutputStream-backed channel!".toByteArray()) channel.flushAndClose() call.respondBytes(out.toByteArray()) }

All operations on the ByteWriteChannel are buffered. The underlying OutputStream receives data only when .flush() is called on the ByteWriteChannel.

29 May 2026