Spring Controller @RequestBody可以上传文件吗?

时间:2014-05-08 05:24:13

标签: spring spring-mvc multipart multipartform-data

我有一个像这样的控制器,我想提交一个包含文件上传的表格以及一些表格数据,如下图所示。另外,我想使用@RequestBody这样做,所以我可以在包装器上使用@Valid注释,因为会添加更多变量。

public @ResponseBody WebResponse<Boolean> updateEUSettings(
    final Locale locale,
    @Validated @ModelAttribute final EUPSettingsWrapper endUserPortalSettingsWrapper) {
}

我的包装是:

public class EUPSettingsWrapper {

    private String label;
    private MultipartFile logo;
// getter , setters..etc...
}

但我想将它从ModelAttributes转换为@RequestBody。

我尝试的方法是将文件上传分隔为请求参数,如下所示:

public @ResponseBody WebResponse<Boolean> updateEUSettings(
    final Locale locale,
    @Validated @RequestBody final EUPSettingsWrapper endUserPortalSettingsWrapper, 
    @RequestParam(value = "file1", required = true) final MultipartFile logo) {

    endUserPortalSettingsWrapper.setLogo(logo);

    // ...
}

在我的模拟MVC中,我正在设置:

getMockMvc().perform(fileUpload(uri).file(logo)
                        .accept(MediaType.APPLICATION_JSON)
                        .content(JSONUtils.toJSON(wrapper))
                        .contentType(MediaType.MULTIPART_FORM_DATA))
                        .andExpect(status().isOk());

但我收到的错误是:

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data' not supported

有没有人知道如何将Multipart文件上传与@RequestBody一起使用?我上面做错了什么?

5 个答案:

答案 0 :(得分:36)

你可以在这里简化你的生活,因为你所做的只是提交一个包含一些字段和文件的表单。 您需要@RequestBody来完成您要执行的操作。您可以使用常规的Spring MVC功能,因此您的控制器方法如下所示:

@ResponseBody
public WebResponse<Boolean> updateEUSettings(
     Locale locale, 
     @Valid EUPSettingsWrapper endUserPortalSettingsWrapper, 
     @RequestParam(value = "file1", required = true) MultipartFile logo
) {


}

将请求提交给此控制器的客户端需要具有enctype="multipart/form-data"的表单。

在你的Spring MVC测试中你会写这样的东西:

getMockMvc().perform(fileUpload(uri).file("file1", "some-content".getBytes()) 
                        .param("someEuSettingsProperty", "someValue")
                        .param("someOtherEuSettingsProperty", "someOtherValue")
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.MULTIPART_FORM_DATA))
                        .andExpect(status().isOk());

答案 1 :(得分:11)

请在spring-servlet.xml中添加以下bean,以添加对多部分请求的支持。

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />

另外不要忘记为 commons-fileupload jar

添加依赖项

答案 2 :(得分:5)

I couldn't find a way to use @RequestBody.

However, you can do something like this:

@RequestMapping(value = "/uploadStuff", method = RequestMethod.POST)
public MyViewDto doStuff(@RequestPart("json") @Valid MyDto dto,
                         @RequestPart("file") MultipartFile file) { ... }

You can test it like this:

MockMultipartFile jsonFile = new MockMultipartFile("json", "",
            "application/json", "{}".getBytes());
MockMultipartFile dataFile = new MockMultipartFile("file", "foo.zip", "application/octet-stream", bytes);

mockMvc.perform(fileUpload("/uploadStuff")
            .file(dataFile)
            .file(jsonFile))
            .andExpect(status().isOk());

答案 3 :(得分:0)

对于Spring 4和更高版本,您可以执行以下操作以获取完整的对象:

  <appender name="JOB_RUN" class="ch.qos.logback.classic.sift.SiftingAppender">

<!-- This is MDC value -->
<!-- We will assign a value to 'jobRunId' via Java code -->
<discriminator>
  <key>jobRunId</key>
  <defaultValue>null</defaultValue>
</discriminator>

<sift>

  <appender name="FILE-${jobRunId}" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logs/${jobRunId}.log</file>
    <prudent>true</prudent>
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%level] [%logger{60}] - %msg%n</pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- daily rollover -->
      <fileNamePattern>logs/${jobRunId}.log.%d{yyyy-MM-dd}</fileNamePattern>
      <!-- keep 14 days of history -->
      <maxHistory>14</maxHistory>
    </rollingPolicy>
  </appender>

</sift>

注意:也可以在没有标签的情况下使用

public ResponseEntity<Object> upload(@Payload EUPSettingsWrapper wrapper) {

}

答案 4 :(得分:-1)

我在这方面做了一点努力,并最终传递了简单的参数。好的,如果你没有多少可以通过你的请求:

myMethod(@RequestParam("file") MultipartFile myFile,
        @RequestParam("param1") Float param1, @RequestParam("param2") String param2 {}