Hi all! I have a theoretical question. I want to s...
# ktor
f
Hi all! I have a theoretical question. I want to send files from multiplatform client to serverver. I saw an example.
Copy code
val result = <http://client.post|client.post><HttpResponse>("<http://127.0.0.1:$port/handler>") {
                    body = MultiPartContent.build {
                        add("user", "myuser")
                        add("password", "password")
                        add("file", byteArrayOf(1, 2, 3, 4), filename = "binary.bin")
                    }
                }
But what will doing if file is big? ByteArraySize could not be more than free RAM. How to send file from IOS and Android to multiplatform without out of memory errors?
r
Here’s a working snippet from my codebase:
Copy code
private suspend fun doUploadFile(data: ByteReadChannel, uploadUrl: String, contentType: ContentType): Unit =
    http.put(uploadUrl) {
        body = object : OutgoingContent.ReadChannelContent() {
            override val contentType = contentType
            override fun readFrom() = data
        }
    }
(Used and working on both Android and iOS)
f
Wow! Great, thank you! I'm going to try now
Can you get me example to call this method from IOS and Android?
r
On Android, with the
Uri
to a local file, the
data
parameter is
appCtx.contentResolver.openInputStream(uri)?.toByteReadChannel() ?: error(...)
For the
ContentType
I’m using
ContentType.fromFilePath(fileName).first()
f
My regards! Thank you so much
For an example for IOS I will be even more grateful
r
I was about to tell you that it’s the same on iOS but after checking it was a little complex:
Copy code
private fun NSURL.toByteReadChannel(errorHandler: (Throwable) -> Unit): ByteReadChannel =
        writer(CoroutineExceptionHandler { _, e -> errorHandler(e) }, true) {
            memScoped {
                val bufferSize = 8192
                val buffer = allocArray<UByteVar>(bufferSize)
                val input = NSInputStream.inputStreamWithURL(this@toByteReadChannel)
                        ?: throw Error("Failed to create NSInputStream from url ${this@toByteReadChannel}")
                var result: Int
                try {
                    input.open()
                    do {
                        @Suppress("EXPERIMENTAL_API_USAGE")
                        result = input.read(buffer, bufferSize.toULong()).toInt()
                        if (result > 0) {
                            channel.writeFully(buffer.readBytes(bufferSize))
                        }
                        if (result < 0) {
                            throw Error("Failed to read inputStream: ${input.streamError?.debugDescription}")
                        }
                    } while (result > 0)
                } finally {
                    channel.close()
                    input.close()
                }
            }
        }.channel
f
You just saved my day. Now I feel like a lazy ass because I haven’t tried it myself
😉