https://kotlinlang.org logo
#ktor
Title
# ktor
a

Alex Styl

03/22/2024, 7:45 AM
Any pointers towards how to create a stream from okio (or any other multiplatform io lib?) so that i can post a file via ktor client? Can't seem to figure out the wiring
a

Aleksei Tirman [JB]

03/22/2024, 8:56 AM
Can you describe the input and the output?
a

Alex Styl

03/22/2024, 9:06 AM
let me share some code
Currently my code looks like this:
Copy code
httpClient.submitFormWithBinaryData(POST_URL,
                                        formData = formData {
                                            val byteArray = source
                                                .use {
                                                    it.buffer()
                                                        .use { bufferFileSource ->
                                                            bufferFileSource.readByteArray()
                                                        }
                                                }

                                            append("file", byteArray, Headers.build {
                                                append(
                                                    HttpHeaders.ContentType,
                                                    ContentType.Image.Any
                                                )
                                                append(
                                                    HttpHeaders.ContentDisposition,
                                                    "filename=\"$fileName\""
                                                )
                                            }
                                            )
                                        }
                                    )
I am concerned about this because it reads the file in one go and I want to support really big files
I am trying to figure out how to go from Okio's
Source
to 'something' (not sure about the API yet) that can upload the file without reading it in one go, to avoid OOM errors
a

Aleksei Tirman [JB]

03/22/2024, 11:34 AM
You can convert the
Source
to
InputStream
and then to
Input
. Here is an example:
Copy code
val source = FileSystem.SYSTEM.source("archive.zip".toPath())
val client = HttpClient(OkHttp)

val response = client.submitFormWithBinaryData("<https://httpbin.org/post>", formData = formData {
    append("file", InputProvider(null) { source.buffer().inputStream().asInput() }, Headers.build {
        append(HttpHeaders.ContentType, ContentType.Image.Any)
        append(HttpHeaders.ContentDisposition, "filename=\"file.bin\"")
    })
})

println(response.bodyAsText())
a

Alex Styl

03/22/2024, 2:39 PM
@Aleksei Tirman [JB] where is
.inputStream()
from? the IDE says it's in the kotlin's stdlib but I added it in the commonMain dependencies and it doesn't pick i tup
a

Aleksei Tirman [JB]

03/22/2024, 2:39 PM
It's from the okio library
a

Alex Styl

03/22/2024, 2:40 PM
Ah I see.
InputStream
is a java API. that is not available for multiplatform
a

Aleksei Tirman [JB]

03/22/2024, 2:41 PM
Ah yeah. That will work only on JVM.
a

Alex Styl

03/22/2024, 2:44 PM
that's a good hint. now i know what I need to convert it to. will have a look how the
.inputStream()
and
.asInput()
works under the hood and will try to do a
buffer().asInput()
tomorrow. If that piece of code already exists somewhere, it would be super helpful
I think I go it. Need to test more but seems to work with a quick test:
Copy code
private fun BufferedSource.asInput(): Input {
    val source = this
    return object : Input() {
        override fun fill(destination: Memory, offset: Int, length: Int): Int {
            val buffer = ByteArrayPool.borrow()
            try {
                val rc = source.read(buffer, 0, minOf(buffer.size, length))
                if (rc == -1) return 0
                destination.storeByteArray(offset, buffer, 0, rc)
                return rc
            } finally {
                ByteArrayPool.recycle(buffer)
            }
        }

        override fun closeSource() {
            source.close()
        }
    }
}
2 Views