Search code examples
javarestfile-uploadspring-bootapache-commons-fileupload

Spring Boot 1.5: accept "multipart/*" in file upload


My Spring Boot 1.5.1 + Commons Fileupload 1.3.2 file upload request mapping fails with multipart/form-data. My request mapping look like this:

@RequestMapping(method=RequestMethod.POST, value="/api/users/uploads")
public @ResponseBody ResponseEntity<?> uploadUsers(
    @RequestParam(value="file", required=true) MultipartFile file
    ) throws IOException {
    String uploadFilename = file.getName();

    try(InputStream input = file.getInputStream()) {
        usersConverter.read(input);
    }

    return ResponseEntity.ok("Uploaded " + uploadFilename + " successfully.");
}

with a curl:

curl -v -u 'username:password'
     -H "Content-Type: multipart/mixed"
     -X POST -F [email protected]
     http://localhost:8080/api/users/uploads

works fine:

*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'username'
> POST /api/users/uploads HTTP/1.1
> Host: localhost:8080
> Authorization: Basic cGFzc3dvcmQ=
> User-Agent: curl/7.42.1
> Content-Length: 8237
> Expect: 100-continue
> Content-Type: multipart/mixed; boundary=-------------------4c6cae60d33268be
> 
< HTTP/1.1 100 Continue
< HTTP/1.1 200 
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Set-Cookie: JSESSIONID=8F80A63F1B83982DA1614ADD7BCB6C16;path=/;HttpOnly
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 27
< Date: Thu, 16 Mar 2017 17:39:51 GMT
< 
* Connection #0 to host localhost left intact

but I have some clients which send multipart/form-data instead of multipart/mixed and I'd like this request mapping to handle both. I've tried adding the following to the @RequestMapping:

  • consumes="multipart/*"
  • consumes={"multipart/mixed", "multipart/form-data"
  • headers="content-type=multipart/*"
  • headers={"content-type=multipart/mixed", "content-type=multipart/form-data"}

but in each case:

curl -v -u 'username:password'
     -H "Content-Type: multipart/form-data"
     -X POST -F [email protected]
     http://localhost:8080/api/users/uploads

fails with a 400:

*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'username'
> POST /api/users/uploads HTTP/1.1
> Host: localhost:8080
> Authorization: Basic cGFzc3dvcmQ=
> User-Agent: curl/7.42.1
> Content-Length: 8237
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=-------------------85d8a22839d9bc08
> 
< HTTP/1.1 100 Continue
< HTTP/1.1 400 
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Set-Cookie: JSESSIONID=1EB31D866907E49F0972F601E22317D0;path=/;HttpOnly
< Content-Length: 0
< Date: Thu, 16 Mar 2017 17:42:00 GMT
< Connection: close
< 
* Closing connection 0

My Angular 2.4 client which sends multipart/form-data fails with (presumably the same) 400.

Any ideas how I get the request mapping to handle multipart/form-data?


Solution

  • Ugh - it's a bug. Remove commons-fileupload and commons-io dependencies:

      <!-- 
      <dependency>
       <groupId>commons-fileupload</groupId>
       <artifactId>commons-fileupload</artifactId>
       <version>1.3.2</version>
      </dependency>
    
      <dependency>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
       <version>2.4</version>
      </dependency>
      -->
    

    and remove multipartResolver bean definition:

        /*
        @Bean(name = "multipartResolver")
        public CommonsMultipartResolver multipartResolver() {
            CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
            multipartResolver.setMaxUploadSize(100000);
    
            return multipartResolver;
        }
        */
    

    then the request mapping works for both multipart/mixed and multipart/form-data as expected...and my Angular client works, too.