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