spring-bootspring-webfluxproject-reactorreactor

How can I read Flux<DataBuffer> content?


I want to read mulitpart/formdata, one part is application/JSON. I can't get them to Map<String,String>, Is there any way to parse Part to String?

private Map<String, String> getFormData(String path, MultiValueMap<String, Part> partMultiValueMap) {
    if (partMultiValueMap != null) {
        Map<String, String> formData = new HashMap<>();
        Map<String, Part> multiPartMap = partMultiValueMap.toSingleValueMap();
        for (Map.Entry<String, Part> partEntry : multiPartMap.entrySet()) {
            Part part = partEntry.getValue();
            if (part instanceof FormFieldPart) {
                formData.put(partEntry.getKey(), ((FormFieldPart) part).value());
            } else {
                String bodyString = bufferToStr(part.content());
                formData.put(partEntry.getKey(), bodyString);

            }
        }
        return formData;
    }
    return null;
}

extra Flux

private String bufferToStr(Flux<DataBuffer> content){
    AtomicReference<String> res = new AtomicReference<>();
    content.subscribe(buffer -> {
        byte[] bytes = new byte[buffer.readableByteCount()];
        buffer.read(bytes);
        DataBufferUtils.release(buffer);
        res.set(new String(bytes, StandardCharsets.UTF_8));
    });
    return res.get();
}

Subscribe is async; bufferToStr value may be null?


Solution

  • You could do it in non-blocking way with StringDecoder

    Basically you could write your code to return Mono<Map<>>

    Note: I'm using Pair class here to return key-value and later collect them to Map

    Pair I'm using here is from package org.springframework.data.util.Pair

    public Mono<Map<String, String>> getFormData(MultiValueMap<String, Part> partMultiValueMap) {
        Map<String, Part> multiPartMap = partMultiValueMap.toSingleValueMap();
    
        return Flux.fromIterable(multiPartMap.entrySet())
                .flatMap(entry -> {
                    Part part = entry.getValue();
                    if (part instanceof FormFieldPart) {
                        return Mono.just(
                                Pair.of(entry.getKey(), ((FormFieldPart) part).value()) // return Pair
                        );
                    } else {
                        return decodePartToString(part.content()) // decoding DataBuffers to string
                                .flatMap(decodedString ->
                                        Mono.just(Pair.of(entry.getKey(), decodedString))); // return Pair
                    }
                })
                .collectMap(Pair::getFirst, Pair::getSecond); // map and collect pairs to Map<>
    }
    
    private Mono<String> decodePartToString(Flux<DataBuffer> dataBufferFlux) {
        StringDecoder stringDecoder = StringDecoder.textPlainOnly();
    
        return stringDecoder.decodeToMono(dataBufferFlux,
                ResolvableType.NONE,
                MimeTypeUtils.TEXT_PLAIN,
                Collections.emptyMap()
        );
    }