4

I would like to implement upload handler method in my Spring Boot app. I want to use CommonsMultipartResolver instead of StandardServletMultipartResolver. I have the the following problem:

My controller method parameter (file) is always set to null. I saw in java docs of HiddenHttpMethodFilter: This filter needs to run after multipart processing in case of a multipart POST request, due to its inherent need for checking a POST body parameter.

I tried to set order for the multipartFilter bean but it did not help. When I debug, I see that HiddenHttpMethodFilter is always called before MultipartFilter. I am using Spring Boot version 1.3.0.RELEASE and Java based config. How can I set the order that HiddenHttpMethodFilter comes after MultipartFilter?

@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
    CommonsMultipartResolver resolver = new CommonsMultipartResolver();        
    resolver.setMaxUploadSizePerFile(52428800); 
    return resolver;
}

@Bean
@Order(0)
public MultipartFilter multipartFilter() {
        MultipartFilter multipartFilter = new MultipartFilter();
    multipartFilter.setMultipartResolverBeanName("multipartResolver");
    return multipartFilter;
}

@RequestMapping(value = "/{userId}", method = RequestMethod.POST)
@ResponseBody
public FileTransport create(@PathVariable("userId") Long userId,
        MultipartFile file) throws IOException {
    //return fileService.create(userId, file);
    return new FileTransport();
}

If I modify the method to include Request param like below:

@RequestParam(name = "file", required = true) MultipartFile file

It throws an exception: 'Required MultipartFile parameter 'file' is not present'.

If I use StandardServletMultipartResolver, everything works as expected.

f.u.
  • 41
  • 1
  • 2
  • 1
    As already [discussed on GitHub](https://github.com/spring-projects/spring-boot/issues/2958#issuecomment-227536841) the order of your `MultipartFilter` is wrong. It needs to be less than the order of `HiddenHttpMethodFilter` which is -10000. If that doesn't help, can you provide a [minimal, complete, verifiable example](http://stackoverflow.com/help/mcve)? – Andy Wilkinson Jun 21 '16 at 19:17
  • @AndyWilkinson the order of HiddenHttpMethodFilter is not -10000, but HIGHEST_PRECEDENCE+10. But even if u set the MultipartFilter ahead of it, uploading fails, this time with a `java.io.IOException: Missing content for multipart request at org.eclipse.jetty.server.Request.extractMultipartParameters(Request.java:390)`. Is CommonsMultipartResolver at all compatible with Spring Boot? – kpentchev Aug 17 '16 at 13:25
  • @AndyWilkinson spring-boot version is 1.2.8. – kpentchev Aug 17 '16 at 13:28
  • @kpentchev You are using a different version of Spring Boot so things may well not be the same. Comments aren't the right place to solve your problem; ask a new question if this one doesn't help. – Andy Wilkinson Aug 17 '16 at 13:55

5 Answers5

7

In Spring Boot to use CommonsMultipartResolver you need to disable MultipartAutoConfiguration by adding following annotation to your configuration:

@EnableAutoConfiguration(exclude={MultipartAutoConfiguration.class})

My multipart resolver bean:

 @Bean
 public MultipartResolver multipartResolver() {
     CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
     multipartResolver.setMaxUploadSize(5 * 1024 * 1024);
     return multipartResolver;
 }
yglodt
  • 13,807
  • 14
  • 91
  • 127
Alex
  • 1,986
  • 22
  • 23
4

I think they have the resolved the issue in Spring-Boot 1.4.2.RELEASE version:

@Bean
public CommonsMultipartResolver multipartResolver() {
    CommonsMultipartResolver multipart = new CommonsMultipartResolver();
    multipart.setMaxUploadSize(3 * 1024 * 1024);
    return multipart;
}

@Bean
@Order(0)
public MultipartFilter multipartFilter() {
    MultipartFilter multipartFilter = new MultipartFilter();
    multipartFilter.setMultipartResolverBeanName("multipartResolver");
    return multipartFilter;
}

You also need to exclude Spring-Boot's MultipartAutoConfiguration.class

@EnableAutoConfiguration(exclude = {MultipartAutoConfiguration.class})

This worked for me.

Jay Modi
  • 245
  • 2
  • 8
0

According to this article it is important to properly craft one's multipart request when using the CommonsMultipartResolver which in turns relies on commons-fileupload. I'll follow the recommendation made in this post. Note how we set the multipart request content-type and content-disposition.

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);

    MultiValueMap<String, String> fileHeaders = new LinkedMultiValueMap<>();
    fileHeaders.add("Content-type", MediaType.TEXT_PLAIN_VALUE);
    fileHeaders.add("Content-disposition", "form-data; name=file; filename=originalFileName.txt");
    File myFile = new File(...);
    HttpEntity multipartFile = new HttpEntity<>(myFile.getBytes(), fileHeaders);

    MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
    form.add("file", multipartFile);

    ResponseEntity<String> response = new RestTemplate().postForEntity("http://localhost:8080/file/", new HttpEntity<>(form, headers), String.class);
    System.err.println(response.toString());

Hope it helps.

Olivier Tonglet
  • 3,312
  • 24
  • 40
0

I was facing the same issue and the below code worked for me :

@Bean(name = "multipartResolver")
public CommonsMultipartResolver createMultipartResolver() {

  final CommonsMultipartResolver cmr = new CommonsMultipartResolver();
  long maxSize = parseSize(multipartConfig.getMaxFileSize());
  cmr.setMaxUploadSize(maxSize);
  cmr.setDefaultEncoding("UTF-8");
  cmr.getFileUpload().setProgressListener(
      (long pBytesRead, long pContentLength, int pItems) -> {
        LOG.info(" Uploaded {}% ", pBytesRead*100/pContentLength);
      });

  return cmr;
}

@Bean
@Order(0)
public MultipartFilter multipartFilter() {
    MultipartFilter multipartFilter = new MultipartFilter();
    multipartFilter.setMultipartResolverBeanName("multipartResolver");
    return multipartFilter;
}
Sushant Verma
  • 899
  • 7
  • 8
0

If anyone is facing problems with PUT requests, you need to extend CommonsMultipartResolver and override the isMultipart method. Then use this new class as the MultipartResolver bean.

public class CustomCommonsMultipartResolver extends CommonsMultipartResolver {
    @Override
    public boolean isMultipart(HttpServletRequest request) {
        String contentType = new ServletRequestContext(request).getContentType();
        if (contentType == null) {
            return false;
        }
        if (contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/")) {
            return true;
        }
        return false;
    }
}
Ehsan Kazi
  • 388
  • 3
  • 8