Java后端单个接口同时接收文件和json数据【全网最详细】

情况

如标题所说

  • 单个请求的请求头中包含Content-Type,form-data中每个参数又有各自的Content-Type,当Content-Type有很多时,最终的请求头Content-Type类似于下面这样。
multipart/form-data; boundary=----WebKitFormBoundaryncBUCgGUvB1AOQsg

image
可以看出,boundary那一串是一个分隔符标志,这个标志是浏览器随机生成的,因此当form-data中类型不同时,我们不应该手动设置Content-Type。

  • 任意一种方法都不需要手动设置Content-Type请求头,JSON blob方法需要为那个json参数设置Content-Type。
  • 文件只能使用@RequestPart注解。按其他的参数的接收方式,分为以下几种方法。
  1. @RequestParam,后端字符串接收,前端传查询字符串(params)
  2. 无注解,后端对象接收
  3. @RequestPart,后端对象接收,前端将Json放到blob文件中,塞到formData里面
  4. @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();
  },

image

后端对象映射(无注解)

    @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
image

对应请求头

image
image

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,
    });

image

参考文章

还有很多文章写得太差而且错误多,值得注意的是,他们的文章中有很多说是用@RequestParam注解接收文件,但是这个注解是给查询参数用的,因此根本无法使用,感觉都没有试过一样。

posted @ 2025-06-01 15:07  魂祈梦  阅读(449)  评论(0)    收藏  举报