简体   繁体   English

如何最好地从 Spring WebClient 的 ClientResponse 获取字节数组?

[英]How to best get a byte array from a ClientResponse from Spring WebClient?

I'm trying out the new WebClient from Spring 5 (5.0.0.RC2) in a codebase that uses reactive programming and I've had success mapping the JSON response from an endpoint to a DTO in my app, which works very nice:我正在使用响应式编程的代码库中尝试使用 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));

However, now I'm trying to the response body from an endpoint which uses Protocol Buffers (binary data served as application/octet-stream ), so I'd like to get the raw bytes from the response, which I'll then map to an object myself.但是,现在我正在尝试从使用协议缓冲区(二进制数据用作application/octet-stream )的端点获取响应正文,因此我想从响应中获取原始字节,然后将其映射自己一个对象。

I got it to work like this using Bytes from Google Guava:我使用来自 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)

This works, but is there an easier, more elegant way to get these bytes?这是有效的,但是有没有更简单、更优雅的方法来获取这些字节?

ClientResponse.bodyToMono() in the end uses some org.springframework.core.codec.Decoder which claims to support the specified class. ClientResponse.bodyToMono()最终使用了一些声称支持指定类的org.springframework.core.codec.Decoder

So we should check the class hierarchy of the Decoder , in particular where and how the decodeToMono() method is implemented.所以我们应该检查Decoder的类层次结构,特别是decodeToMono()方法的实现位置和方式。

There is a StringDecoder which supports decoding to String , a bunch of Jackson-related decoders (used in your DTO example under the hood), and there is also a ResourceDecoder which is of particular interest.有一个StringDecoder支持解码为String ,一堆与 Jackson 相关的解码器(在你的 DTO 示例中使用),还有一个ResourceDecoder是特别有趣的。

ResourceDecoder supports org.springframework.core.io.InputStreamResource and org.springframework.core.io.ByteArrayResource . ResourceDecoder支持org.springframework.core.io.InputStreamResourceorg.springframework.core.io.ByteArrayResource ByteArrayResource is essentially a wrapper around byte[] , so the following code will provide an access to the response body as a byte array: ByteArrayResource本质上是byte[]的包装器,因此以下代码将提供对作为字节数组的响应主体的访问:

Mono<byte[]> mono = client.get()
            ...
            .exchange()
            .flatMap(response -> response.bodyToMono(ByteArrayResource.class))
            .map(ByteArrayResource::getByteArray);

Oleg Estekhin's answer gives the OP what he asked for, but it's loading the entire response content in the memory, which is an issue for large responses. Oleg Estekhin 的回答为 OP 提供了他所要求的内容,但它将整个响应内容加载到内存中,这对于大型响应来说是一个问题。 To get a chunk of bytes at a time instead, we can do the following:要一次获取一大块字节,我们可以执行以下操作:

client.get()
  .uri("uri")
  .exchange()
  .flatMapMany { it.body(BodyExtractors.toDataBuffers()) }

The size of these buffers would be 8192 kb by default;默认情况下,这些缓冲区的大小为 8192 kb; see this answer for changing that if needed.如果需要,请参阅答案以进行更改。

Note that attempting to do dataBuffer.asByteBuffer().array() thrwos an exception if the ByteBuffer is not backed by an array.请注意,如果ByteBuffer不受数组支持,则尝试执行dataBuffer.asByteBuffer().array()会引发异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM