【问题标题】:Spring MVC Multipart Request with JSON带有 JSON 的 Spring MVC 多部分请求
【发布时间】:2014-02-15 06:08:00
【问题描述】:

我想使用 Spring MVC 发布一个包含一些 JSON 数据的文件。所以我开发了一个休息服务作为

@RequestMapping(value = "/servicegenerator/wsdl", method = RequestMethod.POST,consumes = { "multipart/mixed", "multipart/form-data" })
@ResponseBody
public String generateWSDLService(@RequestPart("meta-data") WSDLInfo wsdlInfo,@RequestPart("file") MultipartFile file) throws WSDLException, IOException,
        JAXBException, ParserConfigurationException, SAXException, TransformerException {
    return handleWSDL(wsdlInfo,file);
}

当我从其余客户端发送请求时 content-Type = multipart/form-data or multipart/mixed,我得到下一个异常: org.springframework.web.multipart.support.MissingServletRequestPartException

谁能帮我解决这个问题?

我可以使用@RequestPart 将 Multipart 和 JSON 发送到服务器吗?

【问题讨论】:

  • 您是否在 servlet 上下文中指定了 org.springframework.web.multipart.commons.CommonsMultipartResolver
  • 是的,它已添加到我的 spring.xml 中。

标签: json spring spring-mvc


【解决方案1】:

对于 Angular2+ 用户。尝试在混合部分请求中发送 JSON 有效负载,如下所示。

formData.append("jsonPayload", new Blob([JSON.stringify(json)], {
  type: "application/json"
}));

给出以下完整功能。

submit() {
    const formData = new FormData();
    formData.append('file', this.myForm.get('fileSource').value);
    var json = {
        "key":"value"
    };

  formData.append("jsonPayload", new Blob([JSON.stringify(json)], {
    type: "application/json"
  }));

  this.http.post('http://localhost:8080/api/mixed-part-endpoint', formData)
  .subscribe(res => {
    console.log(res);
    alert('Uploaded Successfully.');
  })
}

【讨论】:

    【解决方案2】:

    我们在我们的项目中看到,带有 JSON 和文件的发布请求在前端和后端开发人员之间造成了很多混乱,从而导致不必要的时间浪费。

    这里有一个更好的方法:将文件字节数组转换为 Base64 字符串并以 JSON 格式发送。

    public Class UserDTO {
        private String firstName;
        private String lastName;
        private FileDTO profilePic; 
    }
    
    public class FileDTO {
        private String base64;
        // just base64 string is enough. If you want, send additional details
        private String name;
        private String type;
        private String lastModified;
    }
    
    @PostMapping("/user")
    public String saveUser(@RequestBody UserDTO user) {
        byte[] fileBytes = Base64Utils.decodeFromString(user.getProfilePic().getBase64());
        ....
    }
    

    将文件转换为base64字符串的JS代码:

    var reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function () {
    
      const userDTO = {
        firstName: "John",
        lastName: "Wick",
        profilePic: {
          base64: reader.result,
          name: file.name,
          lastModified: file.lastModified,
          type: file.type
        }
      }
      
      // post userDTO
    };
    reader.onerror = function (error) {
      console.log('Error: ', error);
    };
    

    【讨论】:

      【解决方案3】:

      这就是我使用 JSON 数据实现 Spring MVC 多部分请求的方式。

      带有 JSON 数据的多部分请求(也称为混合多部分):

      基于 Spring 4.0.2 Release 中的 RESTful 服务,可以通过 @RequestPart 实现 HTTP 请求的第一部分为 XML 或 JSON 格式的数据,第二部分为文件。下面是示例实现。

      Java 代码段:

      Controller 中的 Rest 服务将混合使用 @RequestPart 和 MultipartFile 来服务此类 Multipart + JSON 请求。

      @RequestMapping(value = "/executesampleservice", method = RequestMethod.POST,
          consumes = {"multipart/form-data"})
      @ResponseBody
      public boolean executeSampleService(
              @RequestPart("properties") @Valid ConnectionProperties properties,
              @RequestPart("file") @Valid @NotNull @NotBlank MultipartFile file) {
          return projectService.executeSampleService(properties, file);
      }
      

      前端 (JavaScript) 代码段:

      1. 创建一个 FormData 对象。

      2. 使用以下步骤之一将文件附加到 FormData 对象。

        1. 如果文件已使用“文件”类型的输入元素上传,则将其附加到 FormData 对象。 formData.append("file", document.forms[formName].file.files[0]);
        2. 直接将文件附加到 FormData 对象。 formData.append("file", myFile, "myfile.txt");formData.append("file", myBob, "myfile.txt");
      3. 使用字符串化的 JSON 数据创建一个 blob,并将其附加到 FormData 对象。这会导致多部分请求中第二部分的 Content-type 为“application/json”,而不是文件类型。

      4. 向服务器发送请求。

      5. 请求详情:
        Content-Type: undefined。这会导致浏览器将 Content-Type 设置为 multipart/form-data 并正确填充边界。手动将 Content-Type 设置为 multipart/form-data 将无法填写请求的边界参数。

      Javascript 代码:

      formData = new FormData();
      
      formData.append("file", document.forms[formName].file.files[0]);
      formData.append('properties', new Blob([JSON.stringify({
                      "name": "root",
                      "password": "root"                    
                  })], {
                      type: "application/json"
                  }));
      

      请求详情:

      method: "POST",
      headers: {
               "Content-Type": undefined
        },
      data: formData
      

      请求负载:

      Accept:application/json, text/plain, */*
      Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryEBoJzS3HQ4PgE1QB
      
      ------WebKitFormBoundaryvijcWI2ZrZQ8xEBN
      Content-Disposition: form-data; name="file"; filename="myfile.txt"
      Content-Type: application/txt
      
      
      ------WebKitFormBoundaryvijcWI2ZrZQ8xEBN
      Content-Disposition: form-data; name="properties"; filename="blob"
      Content-Type: application/json
      
      
      ------WebKitFormBoundaryvijcWI2ZrZQ8xEBN--
      

      【讨论】:

      • 做得很好。我不得不使用processData: false, contentType: falseJQuery $ajax()
      • @SunilKumar,如果我需要将文件上传作为可选的..?使用表单数据。我该怎么做。因为如果没有选择图像,我会得到Required request part file is not present
      • 对我来说,"new Blob([JSON.stringify(...)]" 部分做到了。我已经准备好了其他一切。谢谢。
      • @SunilKumar 您是否必须为 ConnectionProperties 指定转换器?如果我对 ConnectionProperties 使用如上所示的 pojo,我会得到...所以不清楚到 POJO 的转换是如何发生的?
      • 只是为了说明这一点:MultipartFile 方法参数上的@NotBlank 注释实际上不会检查文件是否为空。仍然可以上传 0 字节的文档。
      【解决方案4】:

      这一定行得通!

      客户端(角度):

      $scope.saveForm = function () {
            var formData = new FormData();
            var file = $scope.myFile;
            var json = $scope.myJson;
            formData.append("file", file);
            formData.append("ad",JSON.stringify(json));//important: convert to JSON!
            var req = {
              url: '/upload',
              method: 'POST',
              headers: {'Content-Type': undefined},
              data: formData,
              transformRequest: function (data, headersGetterFunction) {
                return data;
              }
            };
      

      后端-Spring Boot:

      @RequestMapping(value = "/upload", method = RequestMethod.POST)
          public @ResponseBody
          Advertisement storeAd(@RequestPart("ad") String adString, @RequestPart("file") MultipartFile file) throws IOException {
      
              Advertisement jsonAd = new ObjectMapper().readValue(adString, Advertisement.class);
      //do whatever you want with your file and jsonAd
      

      【讨论】:

        【解决方案5】:

        如文档所述:

        当“multipart/form-data”请求的一部分由 找不到它的名字。

        这可能是因为请求也不是 multipart/form-data 因为请求中不存在该部件,或者因为网络 应用程序未正确配置以处理多部分 请求——例如没有 MultipartResolver。

        【讨论】:

          猜你喜欢
          • 2017-01-04
          • 2012-08-12
          • 1970-01-01
          • 2019-01-16
          • 2014-03-07
          • 2013-04-20
          • 2015-11-26
          • 2022-01-20
          • 2015-05-04
          相关资源
          最近更新 更多