How to upload multiple files using Webflux?
I already found some solutions. Let's suppose that we send an http POST request with an parameter files which contains our files.
Note responses are arbitrary
RestController with RequestPart
@PostMapping("/upload") public Mono<String> process(@RequestPart("files") Flux<FilePart> filePartFlux) { return filePartFlux.flatMap(it -> it.transferTo(Paths.get("/tmp/" + it.filename()))) .then(Mono.just("OK")); }
RestController with ModelAttribute
@PostMapping("/upload-model") public Mono<String> processModel(@ModelAttribute Model model) { model.getFiles().forEach(it -> it.transferTo(Paths.get("/tmp/" + it.filename()))); return Mono.just("OK"); } class Model { private List<FilePart> files; //getters and setters }
Functional way with HandlerFunction
public Mono<ServerResponse> upload(ServerRequest request) { Mono<String> then = request.multipartData().map(it -> it.get("files")) .flatMapMany(Flux::fromIterable) .cast(FilePart.class) .flatMap(it -> it.transferTo(Paths.get("/tmp/" + it.filename()))) .then(Mono.just("OK")); return ServerResponse.ok().body(then, String.class); }
the key is use toParts instead of toMultipartData, which is more simpler. Here is the example that works with RouterFunctions.
private Mono<ServerResponse> working2(final ServerRequest request) {
final Flux<Void> voidFlux = request.body(BodyExtractors.toParts())
.cast(FilePart.class)
.flatMap(filePart -> {
final String extension = FilenameUtils.getExtension(filePart.filename());
final String baseName = FilenameUtils.getBaseName(filePart.filename());
final String format = LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE);
final Path path = Path.of("/tmp", String.format("%s-%s.%s", baseName, format, extension));
return filePart.transferTo(path);
});
return ServerResponse
.ok()
.contentType(APPLICATION_JSON_UTF8)
.body(voidFlux, Void.class);
}
You can iterate hashmap with Flux and return Flux
Flux.fromIterable(hashMap.entrySet())
.map(o -> hashmap.get(o));
and it will be send as an array with filepart