如何从Spring WebClient的ClientResponse中获取最佳字节数组?

时间:2017-06-29 09:24:06

标签: java spring protocol-buffers reactive-programming binary-data

我在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)

这样可行,但有更简单,更优雅的方法来获取这些字节吗?

2 个答案:

答案 0 :(得分:20)

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

因此,我们应该检查Decoder的类层次结构,特别是decodeToMono()方法的实现位置和方式。

有一个StringDecoder支持解码到String,一堆杰克逊相关的解码器(在你的DTO示例中使用),还有一个ResourceDecoder特别感兴趣。

ResourceDecoder支持org.springframework.core.io.InputStreamResourceorg.springframework.core.io.ByteArrayResourceByteArrayResource本质上是byte[]的包装器,因此以下代码将以字节数组的形式提供对响应主体的访问:

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

答案 1 :(得分:0)

Oleg Estekhin的答案给出了OP所需要的内容,但是它正在将整个响应内容加载到内存中,这对于大型响应是一个问题。要一次获取一大块字节,我们可以执行以下操作:

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

默认情况下,这些缓冲区的大小为8192 kb;请参见this答案以在需要时进行更改。

请注意,如果dataBuffer.asByteBuffer().array()没有由数组支持,则尝试执行ByteBuffer会导致异常。