Spring WebClient:如何将大字节[]流式传输到文件?

似乎Spring无法将响应直接流式传输到文件,而无需将其全部缓冲在内存中。使用较新的Spring 5实现此目的的正确方法是什么?RestTemplateWebClient

WebClient client = WebClient.create("https://example.com");
client.get().uri(".../{name}", name).accept(MediaType.APPLICATION_OCTET_STREAM)
                    ....?

我看到人们已经找到了一些解决方法/技巧来解决这个问题,但我更感兴趣的是用.RestTemplateWebClient

有许多用于下载二进制数据的示例,但几乎所有示例都将加载到内存中。RestTemplatebyte[]


答案 1

使用最近稳定的Spring WebFlux(5.2.4.RELEASE截至撰写本文):

final WebClient client = WebClient.create("https://example.com");
final Flux<DataBuffer> dataBufferFlux = client.get()
        .accept(MediaType.TEXT_HTML)
        .retrieve()
        .bodyToFlux(DataBuffer.class); // the magic happens here

final Path path = FileSystems.getDefault().getPath("target/example.html");
DataBufferUtils
        .write(dataBufferFlux, path, CREATE_NEW)
        .block(); // only block here if the rest of your code is synchronous

对我来说,不明显的部分是,因为它目前在关于Spring文档流的通用部分中提到,在WebClient部分中没有直接引用它。bodyToFlux(DataBuffer.class)


答案 2

我无法测试以下代码是否有效地缓冲了内存中有效负载的内容。尽管如此,我认为你应该从那里开始:webClient

public Mono<Void> testWebClientStreaming() throws IOException {
    Flux<DataBuffer> stream = 
            webClient
                    .get().accept(MediaType.APPLICATION_OCTET_STREAM)
                    .retrieve()
            .bodyToFlux(DataBuffer.class);
    Path filePath = Paths.get("filename");
    AsynchronousFileChannel asynchronousFileChannel = AsynchronousFileChannel.open(filePath, WRITE);
    return DataBufferUtils.write(stream, asynchronousFileChannel)
            .doOnNext(DataBufferUtils.releaseConsumer())
            .doAfterTerminate(() -> {
                try {
                    asynchronousFileChannel.close();
                } catch (IOException ignored) { }
            }).then();
}

推荐