Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have a Java Spring MVC Web application as server. And AngularJS based application as client.

In AngularJS, I have to upload a file and send to server.

Here is my html

<form ng-submit="uploadFile()" class="form-horizontal" enctype="multipart/form-data">
   <input type="file" name="file" ng-model="document.fileInput" id="file" onchange="angular.element(this).scope().setTitle(this)" />
   <input type="text" class="col-sm-4" ng-model="document.title" id="title" />
   <button class="btn btn-primary" type="submit">
         Submit
    </button>
</form>

Here is my UploadController.js

'use strict';

var mainApp=angular.module('mainApp', ['ngCookies']);

mainApp.controller('FileUploadController', function($scope, $http) {

    $scope.document = {};

        $scope.setTitle = function(fileInput) {

        var file=fileInput.value;
        var filename = file.replace(/^.*[\\\/]/, '');
        var title = filename.substr(0, filename.lastIndexOf('.'));
        $("#title").val(title);
        $("#title").focus();
        $scope.document.title=title;
    };

        $scope.uploadFile=function(){
             var formData=new FormData();
         formData.append("file",file.files[0]);
                   $http({
                  method: 'POST',
                  url: '/serverApp/rest/newDocument',
                  headers: { 'Content-Type': 'multipart/form-data'},
                  data:  formData
                })
                .success(function(data, status) {                       
                    alert("Success ... " + status);
                })
                .error(function(data, status) {
                    alert("Error ... " + status);
                });
      };
});

It is going to the server. Here is my DocumentUploadController.java

@Controller
public class DocumentUploadController {

    @RequestMapping(value="/newDocument", headers = "'Content-Type': 'multipart/form-data'", method = RequestMethod.POST)
    public void UploadFile(MultipartHttpServletRequest request, HttpServletResponse response) {

        Iterator<String> itr=request.getFileNames();

        MultipartFile file=request.getFile(itr.next());

        String fileName=file.getOriginalFilename();
        System.out.println(fileName);
    }
}

When I run this I get the following exception

org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found] with root cause
org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
    at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:954)
    at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:331)
    at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:351)
    at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:126)
    at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:156)
    at org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:139)
    at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1047)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:892)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:920)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:827)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:801)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

In my applicationContext.xml, I have mentioned

<bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="100000" />
    </bean>

I am using

spring - 3.2.1.RELAESE
commons-fileupload - 1.2.2
commons-io - 2.4

How to solve this?

It would be great if anyone tel me how to send file and other formdata from angularJS and get it in server.

UPDATE 1

@Michael : I can see this only in the console, when I click submit.

POST http://localhost:9000/serverApp/rest/newDocument 500 (Internal Server Error) angular.js:9499
(anonymous function) angular.js:9499
sendReq angular.js:9333
$http angular.js:9124
$scope.uploadFile invoice.js:113
(anonymous function) angular.js:6541
(anonymous function) angular.js:13256
Scope.$eval angular.js:8218
Scope.$apply angular.js:8298
(anonymous function) angular.js:13255
jQuery.event.dispatch jquery.js:3074
elemData.handle

My server is running in other port 8080. I am uisng yeoman,grunt and bower. So thin gruntfile.js I have mentioned the server port. So it goes to server and running that and throws the exception

UPDATE 2

The boundary is not setting

Request URL:http://localhost:9000/serverApp/rest/newDocument
Request Method:POST
Status Code:500 Internal Server Error

Request Headers view source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:792
Content-Type:multipart/form-data
Cookie:ace.settings=%7B%22sidebar-collapsed%22%3A-1%7D; isLoggedIn=true; loggedUser=%7B%22name%22%3A%22admin%22%2C%22password%22%3A%22admin23%22%7D
Host:localhost:9000
Origin:http://localhost:9000
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
X-Requested-With:XMLHttpRequest
Request Payload
------WebKitFormBoundaryCWaRAlfQoZEBGofY
Content-Disposition: form-data; name="file"; filename="csv.csv"
Content-Type: text/csv


------WebKitFormBoundaryCWaRAlfQoZEBGofY--
Response Headers view source
connection:close
content-length:5007
content-type:text/html;charset=utf-8
date:Thu, 09 Jan 2014 11:46:53 GMT
server:Apache-Coyote/1.1
share|improve this question

4 Answers 4

up vote 4 down vote accepted

Angular will POST as JSON even if you set the Content-Type to the correct value multipart/form-data. So we must transform our request manually to the correct data. Suppos you set your data to FormData as in your code. The only thing to do is provide a transformRequest function that does nothing as returning your original FormData (Angulars implementation transform the FormData to JSON what results in "{}". The server did not understand this as multipart-formdata - there are no boundaries).

so your code should look like this:

$http({
    method: 'POST',
    url: 'newDocument',
    headers: {'Content-Type': 'multipart/form-data'},
    data: formData,
    transformRequest: function(data, headersGetterFunction) {
        return data; // do nothing! FormData is very good!
    }
}).success(function(data, status) {

}).error(function(data, status) {

});

This is the wrong POST Body (without transformRequest) enter image description here

and this is the right POST Body (with transformRequest) enter image description here

share|improve this answer
    
In the server it is showing the same exception. And client alerts with 500 status –  Shiju K Babu Jan 9 '14 at 9:47
    
@ShijuKBabu now it should really work! –  michael Jan 9 '14 at 10:53
    
It is the same exception :( –  Shiju K Babu Jan 9 '14 at 10:56
    
How to set boundary? –  Shiju K Babu Jan 9 '14 at 10:57
1  
Hi I got. Only thing is we need to change the content type to false instead of multipart/form-data in the post. But how to pass both file and other data in the form? –  Shiju K Babu Jan 9 '14 at 12:29

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.

share|improve this answer

You can try this

.js

$scope.uploadFile=function(){
    var formData=new FormData();
    formData.append("file",file.files[0]);
    $http.post('/serverApp/rest/newDocument', formData, {
        transformRequest: function(data, headersGetterFunction) {
            return data;
        },
        headers: { 'Content-Type': undefined }
        }).success(function(data, status) {                       
            alert("Success ... " + status);
        }).error(function(data, status) {
            alert("Error ... " + status);
        });

.java

@Controller
public class DocumentUploadController {
@RequestMapping(value="/newDocument", method = RequestMethod.POST)
    public @ResponseBody void UploadFile(@RequestParam(value="file", required=true) MultipartFile file) {
        String fileName=file.getOriginalFilename();
        System.out.println(fileName);
    }
}

That's based on https://github.com/murygin/rest-document-archive

There is a good example of file upload https://murygin.wordpress.com/2014/10/13/rest-web-service-file-uploads-spring-boot/

share|improve this answer

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;

}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.