Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upload multiple files using Webflux?

How to upload multiple files using Webflux?

I send request with content type: multipart/form-data and body contains one part which value is a set of files.

To process single file I do it as follow:

Mono<MultiValueMap<String, Part> body = request.body(toMultipartData());
body.flatMap(map -> FilePart part = (FilePart) map.toSingleValueMap().get("file"));

But how to done it for multiple files?

PS. Is there another way to upload a set of files in webflux ?

like image 507
Vladlen Gladis Avatar asked Dec 14 '18 11:12

Vladlen Gladis


3 Answers

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

  1. 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"));
    }
    
  2. 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
    }
    
  3. 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);
    }
    
like image 112
Vladlen Gladis Avatar answered Nov 03 '22 05:11

Vladlen Gladis


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

like image 5
Ricard Kollcaku Avatar answered Nov 03 '22 05:11

Ricard Kollcaku


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);
}
like image 1
nekperu15739 Avatar answered Nov 03 '22 05:11

nekperu15739