spring webflux multipart file uplad error,Could not find first boundary

173 Views Asked by At

when i use router function,it works fine RouterFunction:

@Bean
public RouterFunction<ServerResponse> configUpload(UploadHandler uploadHandler) {
    return RouterFunctions.route(RequestPredicates.POST("/api/v1/config/upload").and(RequestPredicates.contentType(MediaType.MULTIPART_FORM_DATA)), uploadHandler::uploadConfig);
}

UploadHandler:

public Mono<ServerResponse> uploadConfig(ServerRequest request){
    return request.body(BodyExtractors.toMultipartData()).flatMap(parts->{
        Map<String, Part> stringPartMap = parts.toSingleValueMap();
        stringPartMap.forEach((partName,value)->{
            if(value instanceof FilePart){
                // Handle file
                FilePart file = (FilePart) stringPartMap.get(partName);
                DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("File name: {}", file.filename());
            }
            if(value instanceof FormFieldPart){
                // Handle form field
                FormFieldPart type = (FormFieldPart) stringPartMap.get(partName);
                DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("type value: {}", type.value());
            }
        });


        return Mono.empty();
    }).then(ServerResponse.ok().bodyValue(Result.success(new MultiFileUploadResponse())));
}

But when i use RestController with RequestBody it give me a parse error: Could not find first boundary

reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Failed to read HTTP message"; nested exception is org.springframework.core.codec.DecodingException: Could not find first boundary Caused by: org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Failed to read HTTP message"; nested exception is org.springframework.core.codec.DecodingException: Could not find first boundary at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.handleReadError(AbstractMessageReaderArgumentResolver.java:235) ~[spring-webflux-5.3.27.jar:5.3.27] at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.lambda$readBody$0(AbstractMessageReaderArgumentResolver.java:185) ~[spring-webflux-5.3.27.jar:5.3.27] at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94) ~[reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$BaseSink.error(FluxCreate.java:474) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:802) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$BufferAsyncSink.error(FluxCreate.java:747) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$SerializedFluxSink.drainLoop(FluxCreate.java:237) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$SerializedFluxSink.drain(FluxCreate.java:213) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$SerializedFluxSink.error(FluxCreate.java:189) [reactor-core-3.4.29.jar:3.4.29] at org.springframework.http.codec.multipart.PartGenerator.hookOnError(PartGenerator.java:176) [spring-web-5.3.27.jar:5.3.27] at reactor.core.publisher.BaseSubscriber.onError(BaseSubscriber.java:180) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$BaseSink.error(FluxCreate.java:474) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:802) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$BufferAsyncSink.error(FluxCreate.java:747) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$SerializedFluxSink.drainLoop(FluxCreate.java:237) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$SerializedFluxSink.drain(FluxCreate.java:213) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxCreate$SerializedFluxSink.error(FluxCreate.java:189) [reactor-core-3.4.29.jar:3.4.29] at org.springframework.http.codec.multipart.MultipartParser.emitError(MultipartParser.java:179) [spring-web-5.3.27.jar:5.3.27] at org.springframework.http.codec.multipart.MultipartParser$PreambleState.onComplete(MultipartParser.java:321) [spring-web-5.3.27.jar:5.3.27] at org.springframework.http.codec.multipart.MultipartParser.hookOnComplete(MultipartParser.java:124) [spring-web-5.3.27.jar:5.3.27] at reactor.core.publisher.BaseSubscriber.onComplete(BaseSubscriber.java:197) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) [reactor-core-3.4.29.jar:3.4.29] at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) [reactor-core-3.4.29.jar:3.4.29] at reactor.netty.channel.FluxReceive.disposeAndUnsubscribeReceiver(FluxReceive.java:499) [reactor-netty-core-1.0.32.jar:1.0.32] at reactor.netty.channel.FluxReceive.lambda$new$0(FluxReceive.java:86) [reactor-netty-core-1.0.32.jar:1.0.32] at reactor.netty.channel.FluxReceive.cancelReceiver(FluxReceive.java:199) [reactor-netty-core-1.0.32.jar:1.0.32] at reactor.netty.channel.FluxReceive.doCancel(FluxReceive.java:205) [reactor-netty-core-1.0.32.jar:1.0.32] at reactor.netty.channel.FluxReceive.dispose(FluxReceive.java:118) [reactor-netty-core-1.0.32.jar:1.0.32] at reactor.netty.channel.ChannelOperations.discard(ChannelOperations.java:366) ~[reactor-netty-core-1.0.32.jar:1.0.32] at reactor.netty.http.server.HttpServerOperations.cleanHandlerTerminate(HttpServerOperations.java:764) ~[reactor-netty-http-1.0.32.jar:1.0.32] at reactor.netty.http.server.HttpTrafficHandler.operationComplete(HttpTrafficHandler.java:464) ~[reactor-netty-http-1.0.32.jar:1.0.32] at reactor.netty.http.server.HttpTrafficHandler.operationComplete(HttpTrafficHandler.java:65) ~[reactor-netty-http-1.0.32.jar:1.0.32] at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:590) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:557) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:492) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:636) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:625) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:105) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.PromiseCombiner.tryPromise(PromiseCombiner.java:170) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.PromiseCombiner.access$600(PromiseCombiner.java:35) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.PromiseCombiner$1.operationComplete0(PromiseCombiner.java:62) ~[netty-common-4.1.92.Final.jar:4.1.92.Final] at io.netty.util.concurrent.PromiseCombiner$1.operationComplete(PromiseCombiner.java:44) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]

Controller code is:

@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<Result<MultiFileUploadResponse>> upload(@RequestBody Mono<MultiValueMap<String, Part>> multiValueMapMono) {
    multiValueMapMono.subscribe(stringPartMap -> {
        stringPartMap.forEach((partName, value) -> {
            if (value instanceof FilePart) {
                // Handle file
                FilePart file = (FilePart) stringPartMap.get(partName);
                DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("File name: {}", file.filename());
            }
            if (value instanceof FormFieldPart) {
                // Handle form field
                FormFieldPart type = (FormFieldPart) stringPartMap.get(partName);
                DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("type value: {}", type.value());
            }
        });
    });
    return Mono.just(Result.success(new MultiFileUploadResponse()));
}

@PostMapping(value = "/upload2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<Result<MultiFileUploadResponse>> upload2(@RequestBody Flux<Part> input) {
    input.subscribe(value -> {
        if (value instanceof FilePart) {
            // Handle file
            FilePart file = (FilePart) value;
            DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("File name: {}", file.filename());
        }
        if (value instanceof FormFieldPart) {
            // Handle form field
            FormFieldPart type = (FormFieldPart) value;
            DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("type value: {}", type.value());
        }
    });
    return Mono.just(Result.success(new MultiFileUploadResponse()));
}

Both Mono<MultiValueMap<String, Part>> or Flux<Part> get error

Spring webflux version:

org.springframework.boot spring-boot-starter-webflux 2.7.12

I have read the spring reference and confirm it support https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/web-reactive.html#webflux-multipart-forms

0

There are 0 best solutions below