Spring MVC - AngularJS - File Upload - org.apache.commons.fileupload.FileUploadException

I faced the same issue and encountered the same issue even after updating the transformRequest. 'Some how, the header boundary doesn't seem to have set correctly.

Following http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs, the problem is resolved. Extract from the location....

By setting ‘Content-Type’: undefined, the browser sets the Content-Type to multipart/form-data for us and fills in the correct boundary. Manually setting ‘Content-Type’: multipart/form-data will fail to fill in the boundary parameter of the request.

Not sure if this helps any one but perhaps makes it easy for people looking at this post... At least, it makes it less difficult.


Introduction

I have had the same problem and found a complete solution to send both json and file from angular based page to a Spring MVC method.

The main problem is the $http which doesn't send the proper Content-type header (I will explain why).

The theory about multipart/form-data

To send both json and file we need to send a multipart/form-data, which means "we send different items in the body separated by a special separator". This special separator is called "boundary", which is a string that is not present in any of the elements that are going to be sent.

The server needs to know which boundary is being used so it has to be indicated in the Content-type header (Content-Type multipart/form-data; boundary=$the_boundary_used).

So... two things are needed:

  1. In the header --> indicate multipart/form-data AND which boundary is used (here is where $http fails)
  2. In the body --> separate each request parameter with the boundary

Example of a good request:

header

Content-Type    multipart/form-data; boundary=---------------------------129291770317552

Which is telling the server "I send a multipart message with the next separator (boundary): ---------------------------129291770317552

body

-----------------------------129291770317552 Content-Disposition: form-data; name="clientInfo" 
{ "name": "Johny", "surname":"Cash"} 

-----------------------------129291770317552 
Content-Disposition: form-data; name="file"; filename="yourFile.pdf"           
Content-Type: application/pdf 
%PDF-1.4 
%õäöü 
-----------------------------129291770317552 --

Where we are sending 2 arguments, "clientInfo" and "file" separated by the boundary.

The problem

If the request is sent with $http, the boundary is not sent in the header (point 1), so Spring is not able to process the data (it doesn't know how to split the "parts" of the request).

The other problem is that the boundary is only known by the FormData... but FormData has no accesors so it's impossible to know which boundary is being used!!!

The solution

Instead of using $http in js you should use standard XMLHttpRequest, something like:

//create form data to send via POST
var formData=new FormData();

console.log('loading json info');
formData.append('infoClient',angular.toJson(client,true)); 
// !!! when calling formData.append the boundary is auto generated!!!
// but... there is no way to know which boundary is being used !!!

console.log('loading file);
var file= ...; // you should load the fileDomElement[0].files[0]
formData.append('file',file);

//create the ajax request (traditional way)
var request = new XMLHttpRequest();
request.open('POST', uploadUrl);
request.send(formData);

Then, in your Spring method you could have something like:

@RequestMapping(value = "/", method = RequestMethod.POST)
public @ResponseBody Object newClient(
        @RequestParam(value = "infoClient") String infoClientString,
        @RequestParam(value = "file") MultipartFile file) {

    // parse the json string into a valid DTO
    ClientDTO infoClient = gson.fromJson(infoClientString, ClientDTO.class);
    //call the proper service method
    this.clientService.newClient(infoClient,file);

    return null;

}

Carlos Verdes's answer failed to work with my $http interceptor, which adds authorization headers and so on. So I decided to add to his solution and create mine using $http.

Clientside Angular (1.3.15)

My form (using the controllerAs syntax) is assuming a file and a simple object containing the information we need to send to the server. In this case I'm using a simple name and type String property.

<form>
    <input type="text" ng-model="myController.myObject.name" />

     <select class="form-control input-sm" ng-model="myController.myObject.type"
      ng-options="type as type for type in myController.types"></select>

     <input class="input-file" file-model="myController.file" type="file">

</form>

The first step was to create a directive that binds my file to the scope of the designated controller (in this case myController) so I can access it. Binding it directly to a model in your controller won't work as the input type=file isn't a built-in feature.

.directive('fileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;

            element.bind('change', function(){
                scope.$apply(function(){
                    modelSetter(scope, element[0].files[0]);
                });
            });
        }
    };
}]);

Secondly I created a factory called myObject with an instance method create that allows me to transform the data upon invoking create on the server. This method adds everything to a FormData object and converts it using the transformRequest method (angular.identity). It is crucial to set your header to undefined. (Older Angular versions might require something than undefined to be set). This will allow the multidata/boundary marker to be set automatically (see Carlos's post).

  myObject.prototype.create = function(myObject, file) {
        var formData = new FormData();
        formData.append('refTemplateDTO', angular.toJson(myObject));
        formData.append('file', file);

        return $http.post(url, formData, {
            transformRequest: angular.identity,
            headers: {'Content-Type': undefined }
        });
}

All that is left to do client side is instantiating a new myObject in myController and invoking the create method in the controller's create function upon submitting my form.

this.myObject = new myObject();

this.create = function() {
        //Some pre handling/verification
        this.myObject.create(this.myObject, this.file).then(
                              //Do some post success/error handling
                            );
    }.bind(this);

Serverside Spring (4.0)

On the RestController I can now simply do the following: (Assuming we have a POJO MyObject)

@RequestMapping(method = RequestMethod.POST)
@Secured({ "ROLE_ADMIN" }) //This is why I needed my $httpInterceptor
public void create(MyObject myObject, MultipartFile file) {
     //delegation to the correct service
}

Notice, I'm not using requestparameters but just letting spring do the JSON to POJO/DTO conversion. Make sure you got the MultiPartResolver bean set up correctly too and added to your pom.xml. (And Jackson-Mapper if needed)

spring-context.xml

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="268435456" /> <!-- 256 megs -->
</bean>

pom.xml

<dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>${commons-fileupload.version}</version> 
</dependency>