带有JSON的Spring MVC分段请求


80

我想使用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发送到服务器吗?


org.springframework.web.multipart.commons.CommonsMultipartResolver在Servlet上下文中指定了吗?
Will Keeling 2014年

是的,它被添加到我的spring.xml中。<bean id =“ multipartResolver” class =“ org.springframework.web.multipart.commons.CommonsMultipartResolver”> <property name =“ maxUploadSize” value =“ 300000000” /> </ bean>
Sunil Kumar,

Answers:


194

这就是我用JSON数据实现Spring MVC Multipart Request的方式。

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

基于Spring 4.0.2版本中的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对象。这将导致multipart请求中第二部分的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--

1
做得很好。我不得不用processData: false, contentType: falseJQuery $ajax()
sura2k

1
@SunilKumar,如果我需要使用Data表格将文件上传作为可选的..?,我该怎么做。因为如果未选择图像,则会越来越多Required request part file is not present
Hema

1
对我来说,“新Blob([JSON.stringify(...)]”)部分做到了。.我已经准备好了其他所有东西
。Thx。– Ostati

4
@SunilKumar是否必须为ConnectionProperties指定Converter?如果我对连接属性使用上面显示的pojo,则会得到... HttpMediaTypeNotSupportedException:不支持内容类型'application / octet-stream'如果将POJO更改为String,则可以正常工作。因此,尚不清楚如何转换为POJO?
user2412398

1
只是为了清楚@NotBlank起见:MultipartFile方法参数上的注释实际上不会检查文件是否为空。仍然可以上传0字节的文档。
sn42

14

这必须工作!

客户(角度):

$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;
        }
      };

后端春季启动:

@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

1

如文档所述:

在找不到以其名称标识的“ multipart / form-data”请求的一部分时引发。

这可能是因为请求不是多部分/表单数据,或者是因为该部分不存在于请求中,或者是因为未正确配置Web应用程序以处理多部分请求-例如,没有MultipartResolver。


0

我们在项目中已经看到,带有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());
    ....
}

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

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);
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.