Aleksei Tirman [JB]
02/27/2025, 11:33 AMursus
02/27/2025, 11:36 AMbodyAsChannel to download a file?ursus
02/27/2025, 11:38 AMByteReadChannel
https://api.ktor.io/ktor-client/ktor-client-core/io.ktor.client.statement/body-as-channel.htmlAleksei Tirman [JB]
02/27/2025, 11:41 AMByteReadChannel to save a response body to a file:
client.get("<https://example.com>").bodyAsChannel()
.copyTo(File("/path/to/file").writeChannel())ursus
02/27/2025, 11:42 AM.use { .. } ?Aleksei Tirman [JB]
02/27/2025, 11:44 AMursus
02/27/2025, 11:44 AMursus
02/27/2025, 11:45 AMAleksei Tirman [JB]
02/27/2025, 11:45 AMcopyAndClose instead of copyTo to close the ByteWriteChannel associated with the file.ursus
02/27/2025, 11:46 AMursus
02/27/2025, 11:47 AMSource? I read that is sort of the InputStream of the kotlinx.io? Isn't the channel an unnecessary abstraction?Aleksei Tirman [JB]
02/27/2025, 4:50 PMwhat if it throws midway?Then the channel will be closed.
Aleksei Tirman [JB]
02/27/2025, 4:53 PMByteReadChannel unlike the Source provides suspendable read operations.ursus
02/27/2025, 5:54 PM@OptIn(InternalAPI::class)
public suspend fun ByteReadChannel.copyAndClose(channel: ByteWriteChannel): Long {
var result = 0L
try {
while (!isClosedForRead) {
result += readBuffer.transferTo(channel.writeBuffer)
channel.flush()
awaitContent()
}
closedCause?.let { throw it }
} catch (cause: Throwable) {
cancel(cause)
channel.close(cause)
throw cause
} finally {
channel.flushAndClose()
}
return result
}Aleksei Tirman [JB]
02/28/2025, 8:52 AMcancel(cause) call in the beginning of the catch block.