Как в Ktor передать поток InputStream в тело запроса HttpClient?

Я использую Ktor 1.2.2, и у меня есть объект InputStream, который я хочу использовать в качестве тело для запроса HttpClient я делаю в дальнейшем. До Ktor 0.95 существовал этот Объект InputStreamContent, который, казалось, делал именно это, но был удален из Ktor в версии 1.0.0 (к сожалению, не мог понять, почему).

Я могу заставить его работать с помощью ByteArrayContent (см. Код ниже), но я бы предпочел найти решение, которое не требует загрузки всего InputStream в память ...

ByteArrayContent(input.readAllBytes())

Этот код представляет собой простой тестовый пример, имитирующий то, что я пытаюсь достичь:

val file = File("c:\\tmp\\foo.pdf")
val inputStream = file.inputStream()
val client = HttpClient(CIO)
client.call(url) {
      method = HttpMethod.Post
      body = inputStream // TODO: Make this work :(
    }
// [... other code that uses the response below]

Сообщите мне, если я пропустил важную информацию,

Спасибо!


person spoissant    schedule 21.06.2019    source источник


Ответы (3)


Единственный API (который я нашел ...) в Ktor 1.2.2 потенциально отправляет многостраничный запрос, для чего ваш принимающий сервер должен иметь возможность обрабатывать это, но он поддерживает прямой InputStream.

Из их документов:

val data: List<PartData> = formData {
    // Can append: String, Number, ByteArray and Input.
    append("hello", "world")
    append("number", 10)
    append("ba", byteArrayOf(1, 2, 3, 4))
    append("input", inputStream.asInput())
    // Allow to set headers to the part:
    append("hello", "world", headersOf("X-My-Header" to "MyValue"))
}

При этом я не знаю, как это работает внутри, и, вероятно, все еще загружает в память весь поток.

Метод readBytes буферизирован, поэтому не занимает всю память.

inputStream.readBytes()
inputStream.close()

В качестве примечания, вам по-прежнему необходимо закрыть inputStream с большинством методов на InputStreams.

Источник Ktor: https://ktor.io/clients/http-client/call/requests.html#the-submitform-and-submitformwithbinarydata-methods.

Источник Kotlin: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-input-stream/index.html

person Benjamin Charais    schedule 21.06.2019

Вот что у меня работает на Ktor 1.3.0 для загрузки файлов в GCP:

client.put<Unit> {
    url(url)
    method = HttpMethod.Put
    body = ByteArrayContent(file.readBytes(), ContentType.Application.OctetStream)
}
person Sir Codesalot    schedule 23.02.2020
comment
Вы умны, чтобы все это прочитать в памяти. - person Mihai; 30.11.2020

Один из способов добиться этого - создать подкласс OutgoingContent.WriteChannelContent и укажите его в теле вашего почтового запроса.

Пример может выглядеть так:

class StreamContent(private val pdfFile:File): OutgoingContent.WriteChannelContent() {
    override suspend fun writeTo(channel: ByteWriteChannel) {
        val readChannel = pdfFile.inputStream().channel
        var copiedBytes: Long
        do {
            copiedBytes = readChannel.copyTo(channel, 1024)
        } while (copiedBytes > 0)
    }
    override val contentType = ContentType.Application.Pdf
    override val contentLength: Long = pdfFile.length()
}


// in suspend function
val pdfFile = File("c:\\tmp\\foo.pdf")
val client = HttpClient()
val result = client.post<HttpResponse>("http://upload.url") {
    body = StreamContent(pdfFile)
}
person Stefan    schedule 30.11.2020