我在Project Reactor下载完成后正确保存文件时遇到问题。
class HttpImageClientDownloader implements ImageClientDownloader {
private final ExchangeFunction exchangeFunction;
HttpImageClientDownloader() {
this.exchangeFunction = ExchangeFunctions.create(new ReactorClientHttpConnector());
}
@Override
public Mono<File> downloadImage(String url, Path destination) {
ClientRequest clientRequest = ClientRequest.create(HttpMethod.GET, URI.create(url)).build();
return exchangeFunction.exchange(clientRequest)
.map(clientResponse -> clientResponse.body(BodyExtractors.toDataBuffers()))
//.flatMapMany(clientResponse -> clientResponse.body(BodyExtractors.toDataBuffers()))
.flatMap(dataBuffer -> {
AsynchronousFileChannel fileChannel = createFile(destination);
return DataBufferUtils
.write(dataBuffer, fileChannel, 0)
.publishOn(Schedulers.elastic())
.doOnNext(DataBufferUtils::release)
.then(Mono.just(destination.toFile()));
});
}
private AsynchronousFileChannel createFile(Path path) {
try {
return AsynchronousFileChannel.open(path, StandardOpenOption.CREATE);
} catch (Exception e) {
throw new ImageDownloadException("Error while creating file: " + path, e);
}
}
}
所以我的问题是: DataBufferUtils.write(dataBuffer,fileChannel,0)是否阻塞?
什么时候磁盘很慢?
关于发生ImageDownloadException时会发生什么的第二个问题, 在doOnNext中我想释放给定的数据缓冲区,这是一个适合这种操作的好地方吗?
我想这一行:
.map(clientResponse -> clientResponse.body(BodyExtractors.toDataBuffers()))
可能会阻止......
答案 0 :(得分:1)
这是实现这一目标的另一种(更短)方式:
Flux<DataBuffer> data = this.webClient.get()
.uri("/greeting")
.retrieve()
.bodyToFlux(DataBuffer.class);
Path file = Files.createTempFile("spring", null);
WritableByteChannel channel = Files.newByteChannel(file, StandardOpenOption.WRITE);
Mono<File> result = DataBufferUtils.write(data, channel)
.map(DataBufferUtils::release)
.then(Mono.just(file));
现在DataBufferUtils::write
操作没有阻塞,因为它们对通道使用非阻塞IO。写入这些通道意味着它会将任何可能的内容写入输出缓冲区(即可以写入所有DataBuffer
或仅写入其中的一部分)。
使用Flux::map
或Flux::doOnNext
是正确的选择。但是你是对的,如果发生错误,你仍然要负责释放当前的缓冲区(以及所有剩余的缓冲区)。我们可以在Spring Framework中改进一些内容,请关注SPR-16782。
我没看到你的上一个样本是如何显示阻塞的:所有方法都返回反应类型,没有一个方法阻塞I / O.