如何最好地从 Spring WebClient 的 ClientResponse 获取字节数组?
How to best get a byte array from a ClientResponse from Spring WebClient?
我正在使用反应式编程的代码库中试用来自 Spring 5 (5.0.0.RC2) 的新 WebClient
,我已经成功映射 JSON 从端点到我的应用程序中的 DTO 的响应,效果非常好:
WebClient client = WebClient.create(baseURI);
Mono<DTO> dto = client.get()
.uri(uri)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.flatMap(response -> response.bodyToMono(DTO.class));
但是,现在我正在尝试从使用协议缓冲区(二进制数据用作 application/octet-stream
)的端点访问响应主体,所以我想从响应中获取原始字节,这然后我会自己映射到一个对象。
我使用 Google Guava 的 Bytes
让它像这样工作:
Mono<byte[]> bytes = client.get()
.uri(uri)
.accept(MediaType.APPLICATION_OCTET_STREAM)
.exchange()
.flatMapMany(response -> response.body(BodyExtractors.toDataBuffers()))
.map(dataBuffer -> {
ByteBuffer byteBuffer = dataBuffer.asByteBuffer();
byte[] byteArray = new byte[byteBuffer.remaining()];
byteBuffer.get(byteArray, 0, bytes.length);
return byteArray;
})
.reduce(Bytes::concat)
这可行,但是有没有更简单、更优雅的方法来获取这些字节?
ClientResponse.bodyToMono()
最后使用了一些 org.springframework.core.codec.Decoder
声称支持指定的 class.
所以我们应该检查 Decoder
的 class 层次结构,特别是 decodeToMono()
方法的实施位置和方式。
有一个 StringDecoder
支持解码为 String
,一堆 Jackson 相关的解码器(在你的 DTO 示例中使用),还有一个 ResourceDecoder
特别感兴趣。
ResourceDecoder
支持 org.springframework.core.io.InputStreamResource
和 org.springframework.core.io.ByteArrayResource
。 ByteArrayResource
本质上是 byte[]
的包装器,因此以下代码将提供对作为字节数组的响应主体的访问:
Mono<byte[]> mono = client.get()
...
.exchange()
.flatMap(response -> response.bodyToMono(ByteArrayResource.class))
.map(ByteArrayResource::getByteArray);
Oleg Estekhin 的回答为 OP 提供了他所要求的内容,但它将整个响应内容加载到内存中,这对于大型响应来说是一个问题。要一次获取一大块字节,我们可以执行以下操作:
client.get()
.uri("uri")
.exchange()
.flatMapMany { it.body(BodyExtractors.toDataBuffers()) }
这些缓冲区的大小默认为 8192 kb;如果需要,请参阅 更改答案。
请注意,如果 ByteBuffer
没有数组支持,则尝试执行 dataBuffer.asByteBuffer().array()
会抛出异常。
我正在使用反应式编程的代码库中试用来自 Spring 5 (5.0.0.RC2) 的新 WebClient
,我已经成功映射 JSON 从端点到我的应用程序中的 DTO 的响应,效果非常好:
WebClient client = WebClient.create(baseURI);
Mono<DTO> dto = client.get()
.uri(uri)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.flatMap(response -> response.bodyToMono(DTO.class));
但是,现在我正在尝试从使用协议缓冲区(二进制数据用作 application/octet-stream
)的端点访问响应主体,所以我想从响应中获取原始字节,这然后我会自己映射到一个对象。
我使用 Google Guava 的 Bytes
让它像这样工作:
Mono<byte[]> bytes = client.get()
.uri(uri)
.accept(MediaType.APPLICATION_OCTET_STREAM)
.exchange()
.flatMapMany(response -> response.body(BodyExtractors.toDataBuffers()))
.map(dataBuffer -> {
ByteBuffer byteBuffer = dataBuffer.asByteBuffer();
byte[] byteArray = new byte[byteBuffer.remaining()];
byteBuffer.get(byteArray, 0, bytes.length);
return byteArray;
})
.reduce(Bytes::concat)
这可行,但是有没有更简单、更优雅的方法来获取这些字节?
ClientResponse.bodyToMono()
最后使用了一些 org.springframework.core.codec.Decoder
声称支持指定的 class.
所以我们应该检查 Decoder
的 class 层次结构,特别是 decodeToMono()
方法的实施位置和方式。
有一个 StringDecoder
支持解码为 String
,一堆 Jackson 相关的解码器(在你的 DTO 示例中使用),还有一个 ResourceDecoder
特别感兴趣。
ResourceDecoder
支持 org.springframework.core.io.InputStreamResource
和 org.springframework.core.io.ByteArrayResource
。 ByteArrayResource
本质上是 byte[]
的包装器,因此以下代码将提供对作为字节数组的响应主体的访问:
Mono<byte[]> mono = client.get()
...
.exchange()
.flatMap(response -> response.bodyToMono(ByteArrayResource.class))
.map(ByteArrayResource::getByteArray);
Oleg Estekhin 的回答为 OP 提供了他所要求的内容,但它将整个响应内容加载到内存中,这对于大型响应来说是一个问题。要一次获取一大块字节,我们可以执行以下操作:
client.get()
.uri("uri")
.exchange()
.flatMapMany { it.body(BodyExtractors.toDataBuffers()) }
这些缓冲区的大小默认为 8192 kb;如果需要,请参阅
请注意,如果 ByteBuffer
没有数组支持,则尝试执行 dataBuffer.asByteBuffer().array()
会抛出异常。