为什么在将 Mono<Void> 映射到 Mono<String> 时会收到空字符串?

Fel*_*era 2 java spring spring-webflux

我正在使用 Spring WebFlux 开发 API REST,但在上传文件时遇到问题。它们被存储,但我没有得到预期的返回值。

这就是我所做的:

  1. 接收 Flux<Part>
  2. 将部件转换为文件部件。
  3. 使用 transferTo() 保存部分(这将返回 Mono<Void>)
  4. 使用文件名将 Mono<Void> 映射到 Mono<String>。
  5. 将 Flux<String> 返回给客户端。

我希望返回文件名,但客户端得到一个空字符串。

控制器代码

@PostMapping(value = "/muscles/{id}/image")
public Flux<String> updateImage(@PathVariable("id") String id, @RequestBody Flux<Part> file) {
    log.info("REST request to update image to Muscle");
    return storageService.saveFiles(file);
}
Run Code Online (Sandbox Code Playgroud)

存储服务

public Flux<String> saveFiles(Flux<Part> parts) {
    log.info("StorageService.saveFiles({})", parts);
    return
            parts
            .filter(p -> p instanceof FilePart)
            .cast(FilePart.class)
            .flatMap(file -> saveFile(file));
}

private Mono<String> saveFile(FilePart filePart) {
    log.info("StorageService.saveFile({})", filePart);
    String filename = DigestUtils.sha256Hex(filePart.filename() + new Date());
    Path target = rootLocation.resolve(filename);
    try {
        Files.deleteIfExists(target);
        File file = Files.createFile(target).toFile();

        return filePart.transferTo(file)
                .map(r -> filename);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

Bri*_*zel 5

FilePart.transferTo()return Mono<Void>,它在操作完成时发出信号 - 这意味着反应式Publisher只会发布一个onComplete/onError信号并且在此之前永远不会发布值。

这意味着该map操作从未执行过,因为它只给定了源发布的元素。

您可以返回文件的名称并仍然链接反应式运算符,如下所示:

return part.transferTo(file).thenReturn(part.filename());
Run Code Online (Sandbox Code Playgroud)

Reactor 3.2 开始,禁止block在反应式管道中使用运算符,它甚至在运行时抛出异常

使用subscribe作为替代也不好,因为subscribe会脱钩从请求处理的传输过程,使得在不同的执行顺序的发生。这意味着您的服务器可以完成处理请求并关闭 HTTP 连接,而另一部分仍在尝试读取文件部分以将其复制到磁盘上。这很可能在运行时以微妙的方式失败。