
spring boot 默认不支持直接混合使用 @requestbody(json)和 @requestpart(文件)处理 multipart/form-data 请求,正确做法是将 multipartfile 字段嵌入 dto,并统一使用 @requestpart 接收整个表单数据。
在 Spring Boot REST API 开发中,常需在一个 HTTP 请求中同时提交结构化 JSON 数据(如元信息、配置项)和二进制文件(如图片、PDF)。但若按常规方式在 @PostMapping 中同时声明 @RequestBody 和 @RequestPart,会触发 HTTP 415 Unsupported Media Type 错误——这是因为 @RequestBody 依赖 HttpMessageConverter 解析纯 JSON(application/json),而文件上传必须使用 multipart/form-data,二者媒体类型冲突,Spring 无法自动桥接。
✅ 正确解决方案:将 MultipartFile 作为字段嵌入 DTO,并全程使用 @RequestPart 接收
首先,修改你的数据传输对象(DTO),使其兼容 multipart 表单:
public class PortfolioFileSaveRequestDto {
private String fileName;
private String description;
private String category;
// ⚠️ 关键:直接声明 MultipartFile 字段(非 String 或 byte[])
private MultipartFile file; // 注意:此处不是 final,且需提供 getter/setter
// 构造函数、getter、setter 省略...
}然后,调整 Controller 方法,移除 @RequestBody,改用 @RequestPart 绑定整个 DTO,并确保 consumes 仅指定 multipart/form-data:
@PostMapping(value = "/file/new",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity> createFile(
HttpServletRequest servletRequest,
@RequestPart("request") @Valid PortfolioFileSaveRequestDto request,
@RequestPart("file") MultipartFile file) { // 可选:显式指定 name,与前端字段名一致
List tokenInfo = jwtProvider.authorizeJwt(servletRequest.getHeader(AUTHORIZATION));
// 验证逻辑(如 tokenInfo 是否为空)...
Portfolio portfolio = portfolioService.saveFile(
request,
user.getUsername(),
profile,
request.getFile() // ✅ 从 DTO 中获取文件
);
return ResponseEntity.status(HttpStatus.CREATED)
.body(DefaultResponseDto.success(portfolio));
} ? 前端请求示例(JavaScript Fetch):
const formData = new FormData();
formData.append('request', new Blob([JSON.stringify({
fileName: "report.pdf",
description: "Q3 financial summary",
category: "FINANCE"
})], { type: 'application/json' }));
formData.append('file', document.getElementById('fileInput').files[0]);
fetch('/api/file/new', {
method: 'POST',
body: formData,
// ❌ 不要设置 Content-Type!浏览器会自动设置为 multipart/form-data 并携带 boundary
});⚠️ 注意事项:
- Spring Boot 2.2+ 默认启用 StandardServletMultipartResolver,请确保 spring.servlet.multipart.* 配置合理(如 max-file-size, max-request-size);
- @RequestPart("request") 中的 "request" 必须与前端 FormData.append('request', ...) 的 key 严格一致;
- 若 DTO 中 MultipartFile 字段名为 file,则 @RequestPart("file") 可省略(Spring 会按字段名匹配),但显式声明更健壮;
- 不要尝试用 @RequestBody + @RequestPart 混合注解——Spring 不支持该组合,这是 415 错误的根本原因。
总结:Spring 的 @RequestPart 能原生解析 multipart 表单中的任意部分(包括 JSON 字符串和文件),关键在于将 JSON 数据“序列化为字符串 Blob”后以表单字段形式提交,并由 DTO 承载结构化语义。这种方式既保持接口原子性,又完全符合 HTTP 规范与 Spring 的设计哲学。










