Spring: JSON 数据和文件在同一个请求中

Spring: JSON data and file in the same request

我知道如何使用 MediaType.MULTIPART_FORM_DATA@FormDataParam("file") FormDataBodyPart bodyPart 创建处理文件的端点,但我想知道我是否也可以在该请求中包含 JSON 数据?类似于:

    @POST
    @Path("somepath")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response uploadFileAndJSON(@RequestBody SomeModel someModel,
                                      @FormDataParam("file") FormDataBodyPart bodyPart) { 
         return null;
    }

目前,如果我在以下 Postman 请求的 "raw" 选项卡上添加一些 JSON 数据,我得到 HTTP 415 Unsupported Media Type 可能是因为我指定我消耗 MULTIPART_FORM_DATA 但我也在使用 @RequestBody,它正在寻找 JSON 内容,即 APPLICATION_JSON。那么如何在同一个请求中处理 JSON 数据和文件呢?我知道可以在两个请求中执行此操作,如果可能的话,我只想在一个请求中执行此操作?

是的,您可以将其作为多部分表单数据获取。

你在 angularjs 中会变成这样:

$scope.uploadFile = function () {
                var file = $scope.selectedFile[0];
                $scope.upload = $upload.upload({
                    url: 'api/upload',
                    method: 'POST',
                    data: angular.toJson($scope.model),
                    file: file
                }).progress(function (evt) {
                    $scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total, 10);
                }).success(function (data) {
                    //do something
                });
            };

            $scope.onFileSelect = function ($files) {
                $scope.uploadProgress = 0;
                $scope.selectedFile = $files;
            };
public Response uploadFileAndJSON(@RequestParam("data") String data,
                                      @MultiPartFile("file")File file) { 
  you can data as form data and convert it 
  like you want to your object using Gson jar.       
 return null;
    }

查看 angularjs 代码: Angularjs how to upload multipart form data and a file?

https://puspendu.wordpress.com/2012/08/23/restful-webservice-file-upload-with-jersey/

为什么要同时使用 Spring 和 Jersey 注释?您应该坚持使用适用于框架的注释。由于您使用的是 Jersey,因此应坚持其 its 注释。

下面是您当前的代码和环境需要考虑的事项。

  1. 不能有两个独立的身体。使用您的代码,这就是您希望发生的情况。
  2. 可以 尽管将 JSON 作为 multi-part body 的一部分。为此,您还应该使用 Jersey @FormDataParam

    注释 SomeModel
    @POST
    @Path("somepath")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response uploadFileAndJSON(
             @FormDataParam("model") SomeModel someModel,
             @FormDataParam("file") FormDataBodyPart bodyPart) { 
    }
    
  3. 在 Jersey 配置中,您需要确保注册 MultiPartFeature。如果不这样做,body 将无法反序列化,您将得到异常和错误响应。

  4. 现在是 Postman 问题。你可以看到类似的问题here。问题是 Content-Type 没有为 JSON body 部分设置。例如 body 可能看起来像

    --AaB03x
    Content-Disposition: form-data; name="model"
    
    {"some":"model", "data":"blah"}
    --AaB03x
    Content-Disposition: form-data; name="file"; filename="file1.txt"
    Content-Type: text/plain
    
    ... contents of file1.txt ...
    --AaB03x--
    

    如果您在 Postman 中点击 Preview 按钮,您实际上可以看到 body。问题是 "model" 部分没有 Content-Type,正如您在 "file" 部分中看到的那样。发生这种情况是因为您无法在 Postman 中设置各个部分的 Content-Type。您将看到的那个将从文件扩展名中发现。例如,.txt 文件将使 Postman 将 Content-Type 设置为 text/plain,将 .png 文件设置为 image/png

    如果您查看上面的 link,我建议您可以使用 .json 文件而不是键入数据。当然那只是一个理论。我没有实际测试它。

    在任何情况下,必须设置 Content-Type 以便 Jersey 能够知道将其反序列化为 JSON。如果 .json 文件扩展理论没有成功,那么你可以使用不同的客户端,比如 cURL,我在 link 中展示了一个例子,或者你可以使用 Jersey 客户端来测试,如所见 here.

  5. 请勿在 Postman 中将 Content-Type header 设置为 multipart/form-data。它会在您使用 form-data 时为您设置。我刚看到 post 有人说当你设置 header 时有错误。现在找不到 post,也不是我已经确认的东西,但我会把它留在外面。


更新

因此 OP 能够找到一种方法将 Content-Type: application/json 设置为 "model" 部分。但有时使用 Javascript 客户端时,您无法设置它。所以不会有Content-Type。如果是这种情况,Jersey 将无法反序列化 JSON,因为它不知道它实际上是 JSON 被发送。如果您绝对不能或不知道如何为各个部分设置 Content-Type,您可以采取以下措施。

@POST
@Path("somepath")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@FormDataParam("model") FormDataBodyPart jsonPart,
                                  @FormDataParam("file") FormDataBodyPart bodyPart) { 
     jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
     SomeModel model = jsonPart.getValueAs(SomeModel.class);
}