使用Spring WebFlux如何在响应体中流式传输二进制数据

16

我正在使用Spring WebFlux制作一个项目。

以前,我使用StreamingResponseBody将流式响应发送回客户端,但是在WebFlux中找不到相应的等效方法。

示例:

import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

@GetMapping("/video")
public StreamingResponseBody stream() {
    InputStream videoStream = ...
    StreamingResponseBody res = (os) -> { IOUtils.copy(videoStream, os); }
    return res;
}

WebFlux是否有类似于StreamingResponseBody的东西?或者,我应该导入传统的Spring MVC并将它们混合使用吗?

编辑: 到目前为止,我是通过访问ServerHttpResponse(如下面的示例)来解决它。但我仍然想知道更好的解决方案。

@GetMapping("/video")
fun stream2(response: ServerHttpResponse): Mono<Void> {
    val factory = response.bufferFactory()
    val publisher = videoStream
            .observeVideoParts()
            .map { factory.wrap(it.bytes) }
    return response.writeWith(publisher)
}

1
@Frischling 谢谢你,但这不是重复的问题。当使用基于注释的控制器和请求映射时,使用 ServerResponse 是行不通的。请参见:https://dev59.com/e-k5XIcBkEYKwwoY8eTT#50026023 - ESala
他没有提到 ServerRespons,而是提到了 ServerHttpResponse - Roman T
我在编辑中遇到了一个问题。当我使用writeWith()时,会出现标题可以设置的错误。例如Content-Length。我也无法设置其他标头。 - Roman T
2个回答

3

目前,我发现最好的解决方案是返回一个 ServerHttpResponse

由于ServerHttpResponse只允许写入DataBuffer对象而不是ByteArray对象,因此我编写了一个扩展函数,在写入之前对它们进行了包装:

fun ServerHttpResponse.writeByteArrays(bytes: Flux<ByteArray>): Mono<Void> {
    val factory = this.bufferFactory()
    val dataBuffers = bytes.map { factory.wrap(it) }
    return this.writeWith(dataBuffers)
}

那么一个Flux<ByteArray>可以这样简单地编写:
@GetMapping("/video")
fun stream2(response: ServerHttpResponse): Mono<Void> {
    val videoParts: Flux<ByteArray> = ...
    return response.writeByteArrays(videoParts)
}

我仍然愿意接受其他解决方案。


2
有点晚了。不过,浏览了Spring核心和Webflux的最新文档后,我认为以下内容应该可以工作:
@GetMapping("/stream/{path}")
public Flux<DataBuffer> getVideo(
        @PathVariable("path") String path,
        ServerHttpResponse response
) {
    return DataBufferUtils.read(
            new FileSystemResource(path),
            response.bufferFactory(),
            512
    );
}

我的错,我没有时间测试这种方法。但如果你要给我点踩,请留下评论。也许我可以改进我的答案。 - PeMa
这并不实用,因为它仅适用于磁盘上的文件,并不是通用的OutputStream答案。除非你先将OutputStream流到磁盘上,然后再以这种方式读取...这似乎很麻烦。 - Stmated
2
@Stmated,FileSystemResource是我选择的一个例子,因为它与OP的问题比较接近。但是,DataBufferUtils.read(...)方法可以接受任何Resource实现作为参数。您可能需要查看https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/Resource.html以找到适合您特定情况的实现。 - PeMa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接