如何最好地从Spring WebClient的客户端Response中获取字节数组?

我正在使用反应式编程的代码库中试用Spring 5(5.0.0.RC2)中的新内容,并且我已经成功地将JSON响应从端点映射到我的应用程序中的DTO,这非常好:WebClient

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

我用谷歌番石榴让它像这样工作: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)

这有效,但是有没有更简单,更优雅的方法来获取这些字节?


答案 1

ClientResponse.bodyToMono()最后使用一些声明来支持指定的类。org.springframework.core.codec.Decoder

因此,我们应该检查 的类层次结构,特别是该方法在何处以及如何实现。DecoderdecodeToMono()

有一个支持解码到,一堆与杰克逊相关的解码器(在引擎盖下的DTO示例中使用),还有一个特别感兴趣的解码器。StringDecoderStringResourceDecoder

ResourceDecoder支持 和 。 本质上是一个包装器,因此以下代码将以字节数组的形式提供对响应正文的访问:org.springframework.core.io.InputStreamResourceorg.springframework.core.io.ByteArrayResourceByteArrayResourcebyte[]

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

答案 2

Oleg Estekhin的回答为OP提供了他所要求的东西,但它将整个响应内容加载到内存中,这对于大型响应来说是一个问题。要一次获取一个字节块,我们可以执行以下操作:

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

默认情况下,这些缓冲区的大小将为 8192 kb。如果需要,请参阅答案以进行更改。

请注意,如果 没有数组支持,则尝试执行 thrwos 将出现异常。dataBuffer.asByteBuffer().array()ByteBuffer


推荐