Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
like image 358
Shiju K Babu Avatar asked Jan 09 '14 09:01

Shiju K Babu


People also ask

How would you handle the situation where a user uploads a very large file through a form in your Spring MVC application?

A new attribute "maxSwallowSize" is the key to deal this situation. It should happen when you upload a file which is larger than 2M. Because the 2M is the default value of this new attribute .

What is the use of CommonsMultipartResolver?

Constructor for use as bean. Determines the servlet container's temporary directory via the ServletContext passed in as through the ServletContextAware interface (typically by a WebApplicationContext).

What is MultipartResolver in Spring?

public interface MultipartResolver. A strategy interface for multipart file upload resolution in accordance with RFC 1867. Implementations are typically usable both within an application context and standalone.


2 Answers

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.

like image 132
Anand Vemula Avatar answered Oct 26 '22 18:10

Anand Vemula


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;

}
like image 43
Carlos Verdes Avatar answered Oct 26 '22 16:10

Carlos Verdes