在 Ktor 中,如何将 InputStream 流式传输到 HttpClient 请求的主体中?
In Ktor, how can I stream an InputStream into a HttpClient Request's body?
我正在使用 Ktor 1.2.2 and I have an InputStream object that I want to use as the body for an HttpClient request I make down the line. Up until Ktor 0.95 there was this InputStreamContent 对象,它似乎就是这样做的,但它已从 1.0.0 版的 Ktor 中删除(不幸的是无法弄清楚原因)。
我可以使用 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]
如果我遗漏了任何相关信息,请告诉我,
谢谢!
Ktor 1.2.2 中唯一的 API(我发现...)可能会发送一个 multi-part 请求,这需要您的接收服务器能够处理这个,但它确实支持直接输入流。
来自他们的文档:
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()
请注意,您仍然需要使用 InputStreams 上的大多数方法关闭 inputStream
Kotlin 来源:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-input-stream/index.html
这就是我在 Ktor 1.3.0 上将文件上传到 GCP 的方法:
client.put<Unit> {
url(url)
method = HttpMethod.Put
body = ByteArrayContent(file.readBytes(), ContentType.Application.OctetStream)
}
实现此目的的一种方法是创建 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)
}
我正在使用 Ktor 1.2.2 and I have an InputStream object that I want to use as the body for an HttpClient request I make down the line. Up until Ktor 0.95 there was this InputStreamContent 对象,它似乎就是这样做的,但它已从 1.0.0 版的 Ktor 中删除(不幸的是无法弄清楚原因)。
我可以使用 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]
如果我遗漏了任何相关信息,请告诉我,
谢谢!
Ktor 1.2.2 中唯一的 API(我发现...)可能会发送一个 multi-part 请求,这需要您的接收服务器能够处理这个,但它确实支持直接输入流。
来自他们的文档:
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()
请注意,您仍然需要使用 InputStreams 上的大多数方法关闭 inputStream
Kotlin 来源:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-input-stream/index.html
这就是我在 Ktor 1.3.0 上将文件上传到 GCP 的方法:
client.put<Unit> {
url(url)
method = HttpMethod.Put
body = ByteArrayContent(file.readBytes(), ContentType.Application.OctetStream)
}
实现此目的的一种方法是创建 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)
}