Spring WebFlux Webclient 接收应用程序/八位字节流文件作为 Mono

Posted

技术标签:

【中文标题】Spring WebFlux Webclient 接收应用程序/八位字节流文件作为 Mono【英文标题】:Spring WebFlux Webclient receiving an application/octet-stream file as a Mono 【发布时间】:2018-09-26 17:43:18 【问题描述】:

我正在 Kotlin 中制作一个小型 Spring WebFlux 应用程序的原型。此应用程序需要从远程 REST 端点获取 tar 存档并将其本地存储在磁盘上。听起来很简单。

我首先创建了一个集成测试,它启动了 spring 服务器和另一个 WebFlux 服务器,并带有一个为 tar 存档提供服务的模拟 REST 端点。

测试应该是这样的:

1) 应用程序:GET mock-server/archive

2) 模拟服务器:状态为 200 的响应和正文中的 tar 存档作为附件类型

3) 应用程序:阻塞直到收到所有字节,然后解压缩并使用文件

我遇到的问题是,当我尝试将字节收集到应用程序上的 ByteArray 中时,它会永远阻塞。

我的mock-server/archive 路由到以下函数:

fun serveArchive(request: ServerRequest): Mono<ServerResponse> 
    val tarFile = FileSystemResource(ARCHIVE_PATH)
    assert(tarFile.exists() && tarFile.isFile && tarFile.contentLength() != 0L)
    return ServerResponse
            .ok()
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .contentLength(tarFile.contentLength())
            .header("Content-Disposition", "attachment; filename=\"$ARCHIVE_FNAME\"")
            .body(fromResource(tarFile))

然后我的应用使用以下命令调用它:

private fun retrieveArchive 
    client.get().uri(ARCHIVE_URL).accept(MediaType.APPLICATION_OCTET_STREAM)
            .exchange()
            .flatMap  response ->
                storeArchive(response.bodyToMono())
            .subscribe()


private fun storeArchive(archive: Mono<ByteArrayResource>): Mono<Void> 
    val archiveContentBytes = archive.block() // <- this blocks forever
    val archiveContents = TarArchiveInputStream(archiveContentBytes.inputStream)
    // read archive

我见过How to best get a byte array from a ClientResponse from Spring WebClient?,这就是我尝试使用ByteArrayResource的原因。

当我逐步检查所有内容时,我看到serveArchive 似乎正在工作(断言语句说我正在传递的文件存在并且其中有一些字节)。在retrieveArchive 我得到一个 200 并且可以在.headers 中看到所有适当的信息(内容类型,内容长度都看起来不错)。当我到达 storeArchive 并尝试使用 block 从 Mono 中检索字节时,它只会永远阻塞。

我完全不知道如何调试这样的东西。

【问题讨论】:

【参考方案1】:

您只需从flatMap 返回转换后的正文,使其从Mono&lt;T&gt; 转换为T

client.get().uri(ARCHIVE_URL).accept(MediaType.APPLICATION_OCTET_STREAM)
            .exchange()
            .flatMap  response ->
                response.bodyToMono(ByteArrayResource::class.java)
            
            .map  archiveContentBytes ->
                archiveContentBytes.inputStream
            
            .doOnSuccess  inputStream ->
                //here is you code to do anything with the inputStream
                val archiveContents = TarArchiveInputStream(inputStream)
            
            .subscribe()

【讨论】:

成功了!谢谢!我最终使用了...flatMap response -&gt; response.bodyToMono&lt;ByteArrayResource&gt;() .map storeArchive(it) ,只需将我的storeArchive 签名更改为ByteArrayResource。但现在我实际上正在获取我的字节并能够解压缩存档等。非常感谢!

以上是关于Spring WebFlux Webclient 接收应用程序/八位字节流文件作为 Mono的主要内容,如果未能解决你的问题,请参考以下文章

Spring WebFlux WebClient 弹性和性能

Spring 5 webflux如何在Webclient上设置超时

在 Spring WebFlux webclient 中设置超时

Spring Webflux WebClient

如何模拟 Spring WebFlux WebClient?

如何在 Spring 5 WebFlux WebClient 中设置超时