Search code examples
javafile-uploadspring-webflux

Mono<Void>.flatMap is not getting invoked when it returns Mono<Something>


I'm new to WebFlux. Developing file upload API. I want to save the uploaded file and meta to db and return the saved entity as a response. The lambda expression is not getting invoked in flatMap on filePart.transferTo. I feel using the block() would make using WebFlux pointless. Any suggestions are appreciated.

public Mono<Music> uploadMusic(FilePart filePart) throws IOException {
    File file = File.createTempFile("tmp-", filePart.filename());
    file.deleteOnExit();
    Music music = new Music();
    return filePart.transferTo(file).flatMap((it) -> {
        music.setFilePath(file.getAbsolutePath());
        music.setMeta(new Metadata());
        log.info(String.valueOf(music));
        return this.musicRepository.save(music);
    });
}

When I did snippet test I found that Mono<Void>.flatMap returning Mono<SomeObj> is seems to be the problem.

// Lambda expression not invoked
public Mono<Music> voidToObj(FilePart filePart) throws IOException {
    Music music = new Music();
    return Mono.just(music).then().flatMap(it -> {
        music.setMeta(new Metadata());
        log.info(music.toString());
        return Mono.just(music);
    });
}

// Lambda expression invoked
public Mono<Music> objToObj(FilePart filePart) throws IOException {
    Music music = new Music();
    return Mono.just(music).flatMap(it -> {
        music.setMeta(new Metadata());
        log.info(music.toString());
        return Mono.just(music);
    });
}

Solution

  • Mono<Void> doesn't produce a value because Void is not an object. Basically it's the same as an empty Mono, it just completes without any actual element.

    As per docs https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html

    Mono<Void> should be used for Publisher that just completes without any value.

    So a flatMap, as well as any other method that is supposed to process a value, would never be called after Mono.just(music).then().flatMap.

    But if you just want to update and return the value you already have then you can do:

    return filePart.transferTo(file).then(Mono.defer(() -> {
        music.setFilePath(file.getAbsolutePath());
        music.setMeta(new Metadata());
        log.info(String.valueOf(music));
        // assuming .save returns a publisher
        return this.musicRepository.save(music);
    });