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

why do I receive an empty string when mapping Mono<Void> to Mono<String>?

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

我就是这样做的:

  1. 收到 Flux<零件>
  2. Cast Part 到 FilePart。
  3. 使用 transferTo() 保存零件(这个 return 一个 Mono
  4. 使用文件名将 Mono 映射到 Mono
  5. Return Flux 到客户端。

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

控制器代码

@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);
}

存储服务

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);
    }
}

FilePart.transferTo() returns Mono 即常量空。然后,在那之后的地图从未被执行过。我通过这样做解决了它:

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)
                .doOnSuccess(data -> log.info("do something..."))
                .thenReturn(filename);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

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

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

您可以 return 文件的名称和仍然链式反应运算符,如下所示:

return part.transferTo(file).thenReturn(part.filename());

禁止在反应管道中使用 block 运算符,它 even throws an exception at runtime as of Reactor 3.2

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