为什么将Mono <void>映射到Mono <string>时会收到一个空字符串?

时间:2018-07-19 06:33:36

标签: java spring spring-webflux

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

这就是我的工作

  1. 接收助焊剂
  2. 将零件铸造到FilePart。
  3. 使用transferTo()保存零件(这将返回Mono
  4. 使用文件名将Mono 映射到Mono
  5. 将通量返回给客户端。

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

控制器代码

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

StorageService

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

2 个答案:

答案 0 :(得分:3)

FilePart.transferTo()返回Mono<Void>,它在操作完成时发出信号-这意味着反应性Publisher将仅发布onComplete / onError信号,并且将在此之前不要发布任何值。

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

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

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

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

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

答案 1 :(得分:0)

FilePart.transferTo()返回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);
    }
}