SpringRestTemplate
似乎无法将响应直接流式传输到文件,而不将其全部缓冲在内存中。使用较新的Spring 5WebClient
实现这一点的正确方法是什么?
WebClient client = WebClient.create("https://example.com");
client.get().uri(".../{name}", name).accept(MediaType.APPLICATION_OCTET_STREAM)
....?
我看到人们在restemplate
中找到了一些解决此问题的方法,但我更感兴趣的是使用WebClient
以正确的方式解决此问题。
有许多使用restemplate
下载二进制数据的示例,但几乎所有示例都将字节[]
加载到内存中。
static <R> Mono<R> writeBodyToTempFileAndApply(
final WebClient.ResponseSpec spec,
final Function<? super Path, ? extends R> function) {
return using(
() -> createTempFile(null, null),
t -> write(spec.bodyToFlux(DataBuffer.class), t)
.thenReturn(function.apply(t)),
t -> {
try {
deleteIfExists(t);
} catch (final IOException ioe) {
throw new RuntimeException(ioe);
}
}
);
}
java prettyprint-override">static <R> Mono<R> pipeBodyAndApply(
final WebClient.ResponseSpec spec, final ExecutorService executor,
final Function<? super ReadableByteChannel, ? extends R> function) {
return using(
Pipe::open,
p -> {
final Future<Disposable> future = executor.submit(
() -> write(spec.bodyToFlux(DataBuffer.class), p.sink())
.log()
.doFinally(s -> {
try {
p.sink().close();
log.debug("p.sink closed");
} catch (final IOException ioe) {
throw new RuntimeException(ioe);
}
})
.subscribe(DataBufferUtils.releaseConsumer())
);
return just(function.apply(p.source()))
.log()
.doFinally(s -> {
try {
final Disposable disposable = future.get();
assert disposable.isDisposed();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
},
p -> {
try {
p.source().close();
log.debug("p.source closed");
} catch (final IOException ioe) {
throw new RuntimeException(ioe);
}
}
);
}
我无法测试以下代码是否有效地在内存中缓冲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();
}
使用最近稳定的Spring WebFlux(5.2.4.1版本,截至撰写时):
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
对我来说,不明显的部分是bodyToFlux(DataBuffer.class)
,因为它目前在一个关于Spring文档流的通用部分中提到,在WebClient部分中没有直接引用它。
问题内容: 看起来Spring 不能将响应直接流式传输到文件而不将其全部缓存在内存中。使用较新的Spring 5实现此目标的合适方法是什么? 我看到人们已经找到了解决此问题的一些变通方法/技巧,但是我对使用正确的方法更感兴趣。 有许多用于下载二进制数据的示例,但几乎所有示例都将其加载到内存中。 问题答案: 使用最近稳定的Spring WebFlux(截至撰写时为5.2.4.RELEASE): 对我
问题 如何流传输大文件? 方案 要流传输大文件,需要添加传输译码(Transfer-Encoding)区块头,这样才能一边下载一边显示。否则,浏览器将缓冲所有数据直到下载完毕才显示。 如果这样写:直接修改基础字符串(例中就是j),然后用 yield 返回--是没有效果的。如果要使用 yield,就要向对所有内容使用 yield。因为这个函式此时是一个生成器。(注:具体细节请查看 yield 文档,
问题内容: 我正在尝试设置一个非常基本的html5页面,该页面会加载20MB的.mp4视频。看来,浏览器需要下载整个内容,而不仅仅是播放视频的第一部分并在其余部分进行流传输。 我在搜索时发现的最接近的内容…我尝试了“手刹”和“数据回合”,两者均未发挥作用: 关于如何执行此操作或是否可行的任何想法? 这是我正在使用的代码: 问题答案: 确保moov(元数据)在mdat(音频/视频数据)之前。这也称为
问题内容: 我有一个200MB的文件,想通过下载提供给用户。但是,由于我们希望用户仅下载一次此文件,因此我们这样做: 强制下载。但是,这意味着整个文件必须加载到内存中,这通常不起作用。我们如何以每块kb的速度将文件流式传输给他们? 问题答案: 尝试这样的事情
我正在尝试制作一些包含文件传输的程序。当然,我必须使用字符流进行一些客户机-服务器通信,我还需要使用字节流来传输文件。当我从BufferedReader中使用println方法form PrintWriter和readLine时会出现问题,因为readLine读取行,但在流中留下“\n”,这会在我尝试使用字节流传输文件后导致问题(我不能100%确定这就是问题)。所以我需要摆脱这个角色,我尝试使用R
问题内容: 我正在构建需要扩展的Java服务器。Servlet之一将提供存储在Amazon S3中的图像。 最近,在负载下,我的VM内存不足,这是在我添加了为图像提供服务的代码之后,因此,我很确定流较大的servlet响应会引起我的麻烦。 我的问题是:从数据库或其他云存储中读取时,如何编写Java Servlet来将大型(> 200k)响应流回浏览器,是否有最佳实践? 我考虑过将文件写入本地临时驱