You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

响应式Spring中如何同时接收Flux<Part>与String请求参数

问题原因

你的代码拿不到uploadType值的核心原因有两个:

  1. 方法上的uploadType参数没有加任何绑定注解,Spring WebFlux默认会从URL查询参数(即?uploadType=xxx这种拼接在路径上的参数)里找对应值,但你是把这个参数放在multipart/form-data的请求表单体里的,自然匹配不到,返回null。
  2. 你对@RequestBody绑定的Flux<Part>流直接做了instanceof FilePart过滤,非文件类型的普通表单字段(对应FormFieldPart实现类)全部被你过滤丢弃了,就算手动从流里找也拿不到这个参数。
正确传参&取值方式

Spring WebFlux对multipart请求里的普通表单字段(String、Enum等基础类型)支持两种主流取值方式,按需选择即可:

方式1:用@RequestPart注解直接绑定(最简便)

给普通参数加上@RequestPart注解,指定和表单字段一致的名称,Spring会自动从multipart请求体里提取对应值,Enum类型直接替换参数类型即可,框架会自动做类型转换。
修正后的代码示例:

@RestController
@RequestMapping(path = "/upload")
public class ReactiveUploadResource {
    Logger LOGGER = LoggerFactory.getLogger(ReactiveUploadResource.class);

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Flux<String> uploadHandler(
            @RequestBody Flux<Part> parts,
            // 加@RequestPart注解绑定表单里的uploadType字段,Enum类型直接替换为对应枚举类即可
            @RequestPart("uploadType") String uploadType
    ) {
        return parts
                .filter(part -> part instanceof FilePart)
                .cast(FilePart.class)
                .flatMap(item -> saveFile(item, uploadType));
    }
}

小提示:如果参数是可选非必填,给@RequestPart加required = false属性,或者把参数类型改为Optional<String>/Mono<String>即可。如果你是把参数拼在URL路径后面传的,把注解换成@RequestParam就行。

方式2:手动从Part流中提取参数

如果你不想加额外注解,也可以从Flux<Part>流里自己筛出普通表单字段取值,注意Flux是一次性流,多次消费必须先做缓存,否则流读完就空了。
代码示例:

@RestController
@RequestMapping(path = "/upload")
public class ReactiveUploadResource {
    Logger LOGGER = LoggerFactory.getLogger(ReactiveUploadResource.class);

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Flux<String> uploadHandler(@RequestBody Flux<Part> parts) {
        // 先缓存Part流,避免多次消费导致流耗尽
        Flux<Part> cachedParts = parts.cache();

        // 提取表单中uploadType字段的值
        Mono<String> uploadTypeMono = cachedParts
                .filter(part -> "uploadType".equals(part.name()) && part instanceof FormFieldPart)
                .cast(FormFieldPart.class)
                .map(FormFieldPart::value)
                .single(); // 非必填参数可以换成singleOptional()

        // 提取文件结合参数处理
        return cachedParts
                .filter(part -> part instanceof FilePart)
                .cast(FilePart.class)
                .zipWith(uploadTypeMono.repeat())
                .flatMap(tuple -> saveFile(tuple.getT1(), tuple.getT2()));
    }
}

内容的提问来源于stack exchange,提问作者Masi Boo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 13:03:19