Java后端单个接口同时接收文件和json数据【全网最详细】
情况
如标题所说
- 单个请求的请求头中包含Content-Type,form-data中每个参数又有各自的Content-Type,当Content-Type有很多时,最终的请求头Content-Type类似于下面这样。
multipart/form-data; boundary=----WebKitFormBoundaryncBUCgGUvB1AOQsg

可以看出,boundary那一串是一个分隔符标志,这个标志是浏览器随机生成的,因此当form-data中类型不同时,我们不应该手动设置Content-Type。
- 任意一种方法都不需要手动设置Content-Type请求头,JSON blob方法需要为那个json参数设置Content-Type。
- 文件只能使用@RequestPart注解。按其他的参数的接收方式,分为以下几种方法。
- @RequestParam,后端字符串接收,前端传查询字符串(params)
- 无注解,后端对象接收
- @RequestPart,后端对象接收,前端将Json放到blob文件中,塞到formData里面
- @RequestPart,后端字符串接收
综上所述,前端有3种传输方式,后端有4种定义方式,其中查询字符串方式对应两种后端定义。
WebFlux
我使用了WebFlux而不是Spring MVC,因此你可以看到
@RequestPart("file") FilePart file,这里接收到类型是FilePart而不是MultipartFile,如果你不知道什么是WebFlux或者你没有使用WebFlux,你应该将下面的FilePart替换成MultipartFile类型。
1和2,查询字符串
前端使用查询字符串,后端有两种接收方式,一种映射对象,一种映射字符串。
对应前端
具体实现不重要,核心是将HomeworkSubmission中对应的属性,使用查询字符串(url中问号后面的部分)传递。
后端接收到参数会自动设置HomeworkSubmission对象的属性。
async submit(homeworkId, studentId, file) {
const fullDomain = window.location.protocol + "//" + window.location.host;
const url = new URL(`${fullDomain + API_BASE_URL}/homework/submit`);
url.searchParams.append('homeworkId', '001');
url.searchParams.append('studentId', '002');
const formData = new FormData();
formData.append("file", file);
const response = await fetch(url, {
method: "POST",
headers: {
satoken: localStorage.getItem("token"),
},
body: formData,
});
return await response.json();
},

后端对象映射(无注解)
@PostMapping(value = "/submit")
public void submitHomework(
@RequestPart("file") FilePart file,
HomeworkSubmission data
)
@RequestPart("data") 将这个注解去除了。
后端字符串映射
@PostMapping(value = "/submit")
public void submitHomework(
@RequestPart("file") FilePart file,
@RequestParam("homeworkId") String homeworkId,
@RequestParam("studentId") String studentId
)
3,Json Blob,对象映射
写起来较为麻烦,而且需要单独写一个DTO类型。
后端代码
@PostMapping(value = "/submit")
public void submitHomework(
@RequestPart("file") FilePart file,
@RequestPart("data") HomeworkSubmission data
)
关键代码
@RequestPart,可以自动映射对象,前端需要传递json类型。
对应前端
const msg = {
homeworkId: homeworkId,
studentId: studentId,
};
const formData = new FormData();
formData.append("file", file);
formData.append('data', new Blob([JSON.stringify(msg)], {type: "application/json"}));
const response = await fetch(`${API_BASE_URL}/homework/submit`, {
method: "POST",
headers: {
satoken: localStorage.getItem("token"),
},
body: formData,
});
核心代码是new Blob那一行,将Json数据包装成了文件。
API测试工具
如果使用postman或者APIFox等测试工具来测试接口,需要在Body -> form-data处设定json的那个参数的Content-Type为application/json。

对应请求头


4,字符串映射(推荐)
这种写法最简洁,无论是前端还是后端。
后端代码
@PostMapping(value = "/submit")
public void submitHomework(
@RequestPart("file") FilePart file,
@RequestPart("homeworkId") String homeworkId,
@RequestPart("studentId") String studentId
)
对应前端
const formData = new FormData();
formData.append("file", file);
formData.append("homeworkId", homeworkId);
formData.append('studentId', studentId);
const response = await fetch(`${API_BASE_URL}/homework/submit`, {
method: "POST",
headers: {
satoken: localStorage.getItem("token"),
},
body: formData,
});

参考文章
- Spring Boot 同时接受文件和实体及 Postman 测试实战 https://cloud.tencent.com/developer/article/2471125
- 在 Spring Boot 应用中同时上传文件、JSON和表单数据
https://springdoc.cn/spring-file-upload-json/ - SpringBoot 同时接收表单数据(后端以实体类接收)和文件
https://www.cnblogs.com/ageovb/p/16662694.html
还有很多文章写得太差而且错误多,值得注意的是,他们的文章中有很多说是用@RequestParam注解接收文件,但是这个注解是给查询参数用的,因此根本无法使用,感觉都没有试过一样。

浙公网安备 33010602011771号