kotlin 在Ktor中,如何将InputStream流式传输到HttpClient请求的主体中?

tzcvj98z  于 2023-01-17  发布在  Kotlin
关注(0)|答案(4)|浏览(159)

我正在使用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]

如果我遗漏了任何相关信息请告诉我,
谢谢!

mqkwyuun

mqkwyuun1#

实现这一点的一种方法是创建OutgoingContent.WriteChannelContent的子类,并将其设置为post请求的主体。
示例如下所示:

class StreamContent(private val pdfFile:File): OutgoingContent.WriteChannelContent() {
    override suspend fun writeTo(channel: ByteWriteChannel) {
        pdfFile.inputStream().copyTo(channel, 1024)
    }
    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)
}
xuo3flqw

xuo3flqw2#

Ktor 1.2.2中唯一的API(我发现...)可能发送多部分请求,这需要您的接收服务器能够处理它,但它确实支持直接的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上的大多数方法,仍然需要关闭inputStream
来源: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

n9vozmp4

n9vozmp43#

这是我在Ktor 1.3.0上上传文件到GCP的工作原理:

client.put<Unit> {
    url(url)
    method = HttpMethod.Put
    body = ByteArrayContent(file.readBytes(), ContentType.Application.OctetStream)
}
xkftehaa

xkftehaa4#

我无法从@stefan获得解决方案,下面是我的另一个例子(针对Ktor 2.2.2):

class StreamContent(private val pdfFile: File) : OutgoingContent.ReadChannelContent() {
    override fun readFrom(): ByteReadChannel = pdfFile.readChannel()
    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") {
    setBody(StreamContent(pdfFile))
}

相关问题