You can read from the file asynchronously using the dispatch_read function. The data exposed in the handler can then be written to a ByteWriteChannel
using the pointer to the data object's contents. To read the entire file, the dispatch_read
method should be called multiple times until the returned data pointer is null
. The suspendCancellableCoroutine function is used to transform the dispatch_read
function with a callback to a suspend function.
Here is an example of how you can stream a request body read from a file:
private const val BUFFER_SIZE = 4088
// ...
val queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.convert(), 0u)
val file = "/path/to/file"
val client = HttpClient(Darwin)
val response = client.post("https://httpbin.org/post") {
setBody(bodyFromFile(file, queue))
}
// ...
@OptIn(ExperimentalForeignApi::class)
private fun bodyFromFile(path: String, queue: dispatch_queue_t): OutgoingContent {
suspend fun read(fd: Int): NSData? {
return suspendCancellableCoroutine { continuation ->
dispatch_read(fd, BUFFER_SIZE.toULong(), queue) { dispatchData, _ ->
val data = dispatchData as NSData
continuation.resume(if (data.bytes != null) data else null)
}
}
}
return ChannelWriterContent(contentType = ContentType.defaultForFilePath(path), body = {
val fd = open(path, O_RDWR)
try {
while (true) {
val data = read(fd) ?: break
val bytes: CPointer<ByteVar> = data.bytes!!.reinterpret()
writeFully(bytes, 0, data.length.toInt())
flush()
}
} finally {
close(fd)
}
})
}