f.u.
f.u.

Reputation: 41

MultipartFile is null when I use CommonsMultipartResolver in my Spring Boot app

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.

Upvotes: 4

Views: 8518

Answers (5)

Alex
Alex

Reputation: 2126

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

Upvotes: 7

Ehsan Kazi
Ehsan Kazi

Reputation: 429

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

Upvotes: 0

Sushant Verma
Sushant Verma

Reputation: 929

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

Upvotes: 0

Olivier Tonglet
Olivier Tonglet

Reputation: 3492

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.

Upvotes: 0

Jay Modi
Jay Modi

Reputation: 245

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.

Upvotes: 4

Related Questions