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

Spring Webflux接收MultipartFile报415 UnsupportedMediaType异常排查

解决Spring Webflux控制器文件上传415 UnsupportedMediaTypeStatusException问题

你遇到的核心问题是在Spring Webflux控制器中误用了Servlet环境的MultipartFile类型,Webflux作为非阻塞的响应式框架,需要使用对应的响应式文件上传类型来处理请求,这才导致参数解析阶段就抛出415错误,控制器方法根本没机会执行。

下面是具体的修复步骤:

1. 替换控制器参数类型为Webflux支持的FilePart

将原来的MultipartFile换成org.springframework.http.codec.multipart.FilePart,这是Webflux专门用于单文件上传的响应式类型:

import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RestController
public class UploadController {
    private static final Logger log = LoggerFactory.getLogger(UploadController.class);
    private final StorageService storageService;

    // 构造注入StorageService
    public UploadController(StorageService storageService) {
        this.storageService = storageService;
    }

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<Void> save(@RequestPart("file") FilePart file) {
        log.info("Storing a new file. Received by Controller");
        // 调整storageService的store方法适配FilePart
        return this.storageService.store(file);
    }
}

如果需要处理多文件上传,可以改用Flux<FilePart>:

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<Void> save(@RequestPart("files") Flux<FilePart> files) {
    log.info("Storing multiple files. Received by Controller");
    return files.flatMap(this.storageService::store).then();
}

2. 调整存储服务适配FilePart

FilePart提供了响应式的文件传输方法,你需要修改storageService的store方法来支持它,比如写入本地磁盘的示例:

import org.springframework.http.codec.multipart.FilePart;
import reactor.core.publisher.Mono;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.springframework.stereotype.Service;

@Service
public class StorageService {
    private final Path storageLocation = Paths.get("your-target-storage-directory");

    public Mono<Void> store(FilePart file) {
        // 确保存储目录存在
        try {
            Files.createDirectories(storageLocation);
        } catch (Exception e) {
            return Mono.error(new RuntimeException("Failed to create storage directory", e));
        }
        // 使用响应式的transferTo方法写入文件
        Path targetPath = storageLocation.resolve(file.filename());
        return file.transferTo(targetPath);
    }
}

3. 检查前端Angular请求的正确性

确保你的Angular代码不要手动设置Content-Type为multipart/form-data——浏览器会自动处理这个头部并添加正确的boundary参数,手动设置反而会导致格式错误:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class UploadService {
    constructor(private http: HttpClient) {}

    uploadFile(file: File): Observable<void> {
        const formData = new FormData();
        formData.append('file', file); // 这里的字段名要和控制器@RequestPart的name完全一致

        return this.http.post<void>('/your-upload-endpoint', formData, {
            // 不要添加这行:'Content-Type': 'multipart/form-data'
        });
    }
}

错误原因解释

MultipartFile是基于Servlet API的类型,属于Spring MVC(传统阻塞式Web框架)的范畴。而Spring Webflux是基于Reactive Streams的非阻塞框架,它使用FilePart这类响应式类型来处理文件上传,这样才能和整个非阻塞模型兼容。当你在Webflux控制器中使用MultipartFile时,框架找不到合适的消息阅读器来解析请求,因此抛出了415 Unsupported Media Type异常。

另外,你使用的spring-webflux-5.0.4.RELEASE版本比较老旧,建议升级到较新的稳定版(比如5.3.x或更高),可以避免一些已知的文件上传相关bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:00:48