asceta
asceta

Reputation: 283

Spring WebFlux - FilePart forward via webClient

I went through similar tickets (i.e. How to stream file from Multipart/form-data in Spring WebFlux), but apparently I have slightly different case.

High-level example (no error handling / no 'empty' checks):

Controller:

@ResponseStatus(code = HttpStatus.CREATED)
    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<String> create(
        @RequestPart(value = "images", required = false) final Flux<FilePart> images
    ) {
        ...service-call...
    }

Service:

    public Mono<String> create(
        final Flux<FilePart> images
    ) {
        MultipartBodyBuilder builder = new MultipartBodyBuilder();

        return images
            .filter(part -> Objects.nonNull(part.headers().getContentType()))
            .filter(part ->
                part.headers().getContentType().includes(MediaType.IMAGE_JPEG)
                    || part.headers().getContentType().includes(MediaType.IMAGE_PNG)
            )
            .doOnNext(
                part -> builder
                        .asyncPart("images", part.content(), DataBuffer.class)
                        .filename(part.filename())
            )
            .then(webClient.post()
                .uri(URI_RESOURCE)
                .contentType(MediaType.MULTIPART_FORM_DATA)
                .body(BodyInserters.fromMultipartData(builder.build()))
                .retrieve()
                .bodyToMono(String.class)
            );
    }

the problem is, client does not receive actual "images" :( I added some doOnNext to log the fact that files are added to builder, so I know they are.

I assume it is because of .asyncPart("images", part.content(), DataBuffer.class) but not sure how to avoid / bypass this internal publisher?

As a note, controller expects a Flux based on all recommendations I saw; however switching to plain List<FilePart>, solves the problem, while I think it is an anti-pattern to assume "blocking" request in WebFlux, right?

Upvotes: 2

Views: 2030

Answers (1)

Adem &#199;ırak
Adem &#199;ırak

Reputation: 241

The problem is your then(webClient...) block is already called and MultipartBodyBuilder.build() is executed at the start of the request. You can wrap then part with Mono.defer.

               ...
               .then(Mono.defer(() -> {
                    return webClient.post()
                            .uri(URI_RESOURCE)
                            .contentType(MediaType.MULTIPART_FORM_DATA)
                            .body(BodyInserters.fromMultipartData(builder.build()))
                            .retrieve()
                            .bodyToMono(String.class);
                }));

Upvotes: 2

Related Questions