Java 如何在 Spring RESTful 服务中处理由文件和 JSON 对象组成的多部分请求?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27294838/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 04:07:31  来源:igfitidea点击:

How to process a multipart request consisting of a file and a JSON object in Spring restful service?

javajsonspringrestmultipartform-data

提问by Sami

I have the following resource (implemented using Spring 4.05.RELEASE) which accepts a file and a JSON object:

我有以下资源(使用 Spring 4.05.RELEASE 实现),它接受一个文件和一个 JSON 对象:

(P.S. activityTemplate is a serializable entity class)

(PS activityTemplate 是一个可序列化的实体类)

...
@RequestMapping(value="/create", method=RequestMethod.POST)
public @ResponseBody ActivityTemplate createActivityTemplate(
        @RequestPart ActivityTemplate activityTemplate, @RequestPart MultipartFile jarFile)
{
   //process the file and JSON
}
...

and this is the form I am testing from:

这是我正在测试的表格:

<form method="POST" enctype="multipart/form-data"
    action="http://localhost:8080/activityTemplates/create">
    JSON: <input type="text" name="activityTemplate" value='/* the JSON object*/'><br />

    File to upload: <input type="file" name="file">
    <input type="submit" value="Upload">
</form>

and this is the error that I get:

这是我得到的错误:

 There was an unexpected error (type=Unsupported Media Type, status=415).
 Content type 'application/octet-stream' not supported

So how should I make the resource accept the JSON object as part of the multipart request, or should I be sending the form in a different way?

那么我应该如何让资源接受 JSON 对象作为多部分请求的一部分,或者我应该以不同的方式发送表单?

回答by Sridhar DD

Hope this should help you. You need to set the boundary in your request to inform the HTTP Request. is simple; A brief introduction to the multipart formatcan be found in the below link

希望这对你有帮助。您需要在请求中设置边界以通知 HTTP 请求。很简单;可以在下面的链接中找到多部分格式的简要介绍

HTML 4.01 Specification for multipart

多部分的 HTML 4.01 规范

The following example illustrates "multipart/form-data" encoding. If the Json Objectis "MyJsonObj" , and file that need to be send is "myfile.txt", the user agent might send back the following data:

以下示例说明了“ multipart/form-data”编码。如果Json 对象是“ MyJsonObj”,并且需要发送的文件是“ myfile.txt”,则用户代理可能会发回以下数据:

Content-Type: multipart/form-data; boundary=MyBoundary

--MyBoundary
Content-Disposition: form-data; name="myJsonString"
Content-Type: application/json

MyJsonObj //Your Json Object goes here
--MyBoundary
Content-Disposition: form-data; name="files"; filename="myfile.txt"
Content-Type: text/plain

... contents of myfile.txt ...
--MyBoundary--

or if your files is of type image with name "image.gif" then,

或者如果您的文件是名称为“ image.gif”的图像类型,那么,

--MyBoundary
Content-Disposition: file; filename="image.gif"
Content-Type: image/gif
Content-Transfer-Encoding: binary

...contents of image.gif...
--MyBoundary--

You specify boundaryin the Content-Type headerso that the server knows how to split the data sent.

boundary在 中指定,Content-Type header以便服务器知道如何拆分发送的数据。

So, you basically need to select a boundary value to:

因此,您基本上需要选择一个边界值来:

  • Use a value that won't appear in the HTTP data sent to the server like 'AaB03x'.
  • Be consistent and use the same value all over the request.
  • 使用不会出现在发送到服务器的 HTTP 数据中的值,如'AaB03x'.
  • 保持一致并在整个请求中使用相同的值。

回答by Mithun

The default content type is 'application/octet-stream'. Since you are uploading jar file and JSON the content type should be set in the @RequestMappingannotation as follows:

默认的内容类型是“application/octet-stream”。由于您正在上传 jar 文件和 JSON,因此应在@RequestMapping注释中设置内容类型,如下所示:

@RequestMapping(value="/create", method=RequestMethod.POST, headers="content-type=application/json,application/java-archive")

回答by miw

The error message indicates that there is no HttpMessageConverter registered for a multi-part/MIME part of content type: application/octet-stream. Still, your jarFileparameter is most likely correctly identified as application/octet-stream, so I'm assuming there's a mismatch in the parameter mapping.

该错误消息表明没有为内容类型的多部分/MIME 部分注册 HttpMessageConverter:application/octet-stream。尽管如此,您的jarFile参数很可能被正确识别为 application/octet-stream,因此我假设参数映射中存在不匹配。

So, first try setting the same name for the parameter and the form's input element.

因此,首先尝试为参数和表单的输入元素设置相同的名称。

Another problem is that the JSON is uploaded as a (regular) value of a text input in the form, not as a separate part in the multi-part/MIME. So there's no content-type header associated with it to find out that Spring should use the JSON deserializer. You can use @RequestParaminstead and register a specific converter like in this answer: JSON parameter in spring MVC controller

另一个问题是 JSON 作为表单中文本输入的(常规)值上传,而不是作为多部分/MIME 中的单独部分。因此,没有与之关联的内容类型标头来确定 Spring 应该使用 JSON 反序列化器。您可以@RequestParam改为使用并注册一个特定的转换器,如这个答案:Spring MVC 控制器中的 JSON 参数

回答by codeMan

You have not given the param names to your @RequestParts ?

您还没有为您的@RequestPart提供参数名称?

public @ResponseBody ActivityTemplate createActivityTemplate(
    @RequestPart("activityTemplate") ActivityTemplate activityTemplate, @RequestPart("file") MultipartFile jarFile)
{
   //process the file and JSON
}

Note: do not forget to include the Hymanson mapper .jar (maps your Json to ActivityTemplate) file in your classpath.

注意:不要忘记在类路径中包含 Hymanson mapper .jar(将您的 Json 映射到 ActivityTemplate)文件。

回答by mohi

This took me two days to work for me!

这花了我两天的时间为我工作!

client (angular):

客户端(角度):

$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 string 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

回答by Vogel

Couldn't you change your

你不能改变你的

@RequestMapping(value="/create", method=RequestMethod.POST)

to

@RequestMapping(value="/create",
                method=RequestMethod.POST, consumes ={"multipart/form-data"})

回答by K Kishore Kumar Reddy

this may help you, while receiving MultipartFile you should set request header content-type to "multipart/form-data" , then in your controller use consumes="multipart/form-data" , consumes also used to map our request to our method in controller.

这可能对您有所帮助,在接收 MultipartFile 时,您应该将请求标头内容类型设置为 "multipart/form-data" ,然后在您的控制器中使用consumes="multipart/form-data",consumes 也用于将我们的请求映射到我们的方法在控制器中。

If you want to receive JSON data , better to send request in the form of JSONString , just receive that jsonstring, later convert into json Object format then, use that object for yours operations.

如果你想接收 JSON 数据,最好以 JSONString 的形式发送请求,只接收那个 jsonstring,然后转换成 json 对象格式,然后使用该对象进行操作。

check below code :

检查以下代码:

@RequestMapping(value="/savingImg", method=RequestMethod.POST, 
        consumes="multipart/form-data", produces="application/json")
public ResponseEntity<?> savingAppIMgDtlss(
        @RequestParam(value="f1", required = false) MultipartFile f1 , 
        @RequestParam(value="f2", required = false) MultipartFile f2 ,
        @RequestParam(value="f3", required = false) MultipartFile f3 ,
        @RequestParam(value="f4", required = false) MultipartFile f4 ,
        @RequestParam(value="f5", required = false) MultipartFile f5 ,
        @RequestParam(value="f6", required = false) MultipartFile f6 ,
        @RequestParam(value="f7", required = false) MultipartFile f7 ,
        @RequestParam(value="f8", required = false) MultipartFile f8 ,@RequestParam("data") String jsonString) 
                throws Exception , ParseException {
    try{
        JSONObject gstcTranObj = new JSONObject();
                //converting JSONString to JSON
        net.sf.json.JSONObject jsonDtls = net.sf.json.JSONObject.fromObject(jsonString);
        System.out.println("f1::"+f1.getOriginalFilename());
        System.out.println("f2::"+f2.getOriginalFilename());
        System.out.println("f3::"+f3.getOriginalFilename());
        System.out.println("f4::"+f4.getOriginalFilename());
        System.out.println("f5::"+f5.getOriginalFilename());
        System.out.println("f6::"+f6.getOriginalFilename());
        System.out.println("f7::"+f7.getOriginalFilename());
        System.out.println("f8::"+f8.getOriginalFilename());
} catch (Exception e) {
        e.printStackTrace();

        return new ResponseEntity<>("Failed",HttpStatus.NOT_FOUND);
    }finally{

    }
return new ResponseEntity<>("Success", HttpStatus.OK);

  }
}

回答by Maksim

Exception is thrown because you don't have appropriate HttpMessageConverter, to process multipart/form-data request. Workaround

抛出异常是因为您没有合适的 HttpMessageConverter 来处理 multipart/form-data 请求。 解决方法

回答by Pramod Wayabase

You can use @RequestPart from org.springframework.web.bind.annotation.RequestPart; It is used as Combining @RequestBody and file upload.

您可以使用 org.springframework.web.bind.annotation.RequestPart 中的 @RequestPart; 它用作结合@RequestBody 和文件上传。

Using @RequestParam like this @RequestParam("file") MultipartFile file you can upload only file and multiple single data (key value ) like

像这样使用@RequestParam @RequestParam("file") MultipartFile 文件,您可以只上传文件和多个单个数据(键值),例如

        @RequestMapping(value = "/uploadFile", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
    public void saveFile(
                         @RequestParam("userid") String userid,
                         @RequestParam("file") MultipartFile file) {

    }

you can post JSON Object data and and File both using @RequestPart like

您可以使用@RequestPart 发布 JSON 对象数据和文件,例如

    @RequestMapping(value = "/patientp", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> insertPatientInfo(
                                            @RequestPart PatientInfoDTO patientInfoDTO,
                                            @RequestPart("file") MultipartFile file) {
}

You are not limited to using multipart file uploads directly as controller method parameters. Your form objects can contain Part or MultipartFile fields, and Spring knows automatically that it must obtain the values from file parts and converts the values appropriately.

您不仅限于直接使用分段文件上传作为控制器方法参数。您的表单对象可以包含 Part 或 MultipartFile 字段,并且 Spring 自动知道它必须从文件部分获取值并适当地转换这些值。

Above method can respond to the previously demonstrated multipart request containing a single file. This works because Spring has a built-in HTTP message converter that recognizes file parts. In addition to the javax.servlet.http.Part type, you can also convert file uploads to org.springframework.web.multipart.MultipartFile. If the file field permits multiple file uploads, as demonstrated in the second multipart request, simply use an array or Collection of Parts or MultipartFiles.

上述方法可以响应前面演示的包含单个文件的多部分请求。这是有效的,因为 Spring 有一个内置的 HTTP 消息转换器来识别文件部分。除了 javax.servlet.http.Part 类型之外,您还可以将文件上传转换为 org.springframework.web.multipart.MultipartFile。如果文件字段允许多个文件上传,如第二个多部分请求中所示,只需使用数组或部分集合或 MultipartFiles。

        @RequestMapping(value = "/patientp", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
    public ResponseEntity<?> insertPatientInfo(
                                                @RequestPart PatientInfoDTO patientInfoDTO,
                                                @RequestPart("files") List<MultipartFile> files) {
    }

Happy To Help...

很高兴能帮助你...