복붙노트

[SPRING] Spring MVC - AngularJS - 파일 업로드 - org.apache.commons.fileupload.FileUploadException

SPRING

Spring MVC - AngularJS - 파일 업로드 - org.apache.commons.fileupload.FileUploadException

Java Spring MVC 웹 애플리케이션을 서버로 보유하고 있습니다. AngularJS 기반의 클라이언트 응용 프로그램입니다.

AngularJS에서는 파일을 업로드하고 서버로 보내야합니다.

여기 내 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>

여기 내 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);
                });
      };
});

그것은 서버로갑니다. 여기 내 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);
    }
}

이걸 실행하면 다음과 같은 예외가 발생합니다.

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)

내 applicationContext.xml에서 언급했습니다.

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

나는 사용하고있다.

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

어떻게 해결할 수 있을까요?

어떤 사람이 나에게 전화를해서 angularJS에서 파일과 다른 formdata를 보내고 서버에서 가져 오는 것이 좋다면 좋을 것입니다.

업데이트 1

@ 마이클 : 제출을 클릭하면 콘솔에서만 볼 수 있습니다.

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

내 서버가 다른 포트 8080에서 실행 중입니다. 저는 yeoman, grunt 및 bower를 사용하고 있습니다. 그래서 얇은 gruntfile.js 나는 서버 포트를 언급했다. 그래서 서버로 가서 실행하고 예외를 throw합니다.

업데이트 2

경계가 설정되어 있지 않습니다.

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

해결법

  1. ==============================

    1.동일한 문제가 발생하여 transformRequest를 업데이트 한 후에도 동일한 문제가 발생했습니다. '어떻게하면 헤더 경계가 올바르게 설정되지 않은 것 같습니다.

    동일한 문제가 발생하여 transformRequest를 업데이트 한 후에도 동일한 문제가 발생했습니다. '어떻게하면 헤더 경계가 올바르게 설정되지 않은 것 같습니다.

    http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs에 따라 문제가 해결되었습니다. 위치에서 추출 ....

    'Content-Type'을 설정하면 : 브라우저가 Content-Type을 multipart / form-data로 설정하고 올바른 경계를 채 웁니다. 수동으로 'Content-Type'설정 : multipart / form-data는 요청의 경계 매개 변수를 채우지 못합니다.

    이것이 어떤 것을 돕는 지 확신 할 수 없지만 아마이 게시물을 보는 사람들이 쉽게 만들 수 있습니다 ... 최소한 그것은 어렵지 않습니다.

  2. ==============================

    2.나는 동일한 문제를 겪었으며 json과 파일을 각도 기반 페이지에서 Spring MVC 메서드로 보내는 완벽한 솔루션을 발견했다.

    나는 동일한 문제를 겪었으며 json과 파일을 각도 기반 페이지에서 Spring MVC 메서드로 보내는 완벽한 솔루션을 발견했다.

    가장 큰 문제는 적절한 Content-type 헤더를 보내지 않는 $ http입니다 (이유를 설명 할 것입니다).

    json과 파일을 보내려면 multipart / form-data를 보내야합니다. 이는 "우리는 특별한 구분 기호로 분리 된 다른 항목을 본문에 보냅니다"라는 의미입니다. 이 특수 분리 기호는 "boundary"라고하며, 송신 될 요소에 존재하지 않는 문자열입니다.

    서버는 사용중인 경계를 알아야 Content-type 헤더 (Content-Type multipart / form-data; boundary = $ the_boundary_used)에 표시해야합니다.

    그래서 ... 두 가지가 필요합니다.

    좋은 요청의 예 :

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

    어떤 서버에 "다음 구분 기호 (경계)가있는 다중 부분 메시지를 보냅니다. --------------------------- 129291770317552

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

    여기서 우리는 두 개의 인수 인 "clientInfo"와 "file"을 경계로 구분하여 보냅니다.

    요청이 $ http와 함께 전송되면, 경계는 헤더 (포인트 1)에서 전송되지 않으므로 Spring은 데이터를 처리 할 수 ​​없습니다 (요청의 "부분"을 분할하는 방법을 알지 못함).

    다른 문제는 경계가 FormData ...에 의해서만 알려지기 때문입니다. 그러나 FormData에는 아무런 접근이 없으므로 어떤 경계가 사용되는지 알 수 없습니다 !!!

    js에서 $ http를 사용하는 대신 표준 XMLHttpRequest를 사용해야한다.

    //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);
    

    그런 다음 Spring 메소드에서 다음과 같은 것을 가질 수있다 :

    @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;
    
    }
    
  3. ==============================

    3.Carlos Verdes의 대답은 인증 헤더를 추가하는 $ http 인터셉터와 함께 작동하지 못했습니다. 그래서 나는 그의 솔루션에 추가하고 $ http를 사용하여 광산을 만들기로 결정했습니다.

    Carlos Verdes의 대답은 인증 헤더를 추가하는 $ http 인터셉터와 함께 작동하지 못했습니다. 그래서 나는 그의 솔루션에 추가하고 $ http를 사용하여 광산을 만들기로 결정했습니다.

    내 양식 (controllerAs 구문 사용)은 파일 및 우리가 서버에 보내야하는 정보가 들어있는 간단한 객체를 가정합니다. 이 경우에는 간단한 이름과 String 속성을 사용합니다.

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

    첫 번째 단계는 내 파일을 지정된 컨트롤러 (이 경우 myController)의 범위에 바인딩하는 지시문을 작성하여 액세스 할 수 있도록하는 것이 었습니다. 입력 유형 = 파일이 기본 제공 기능이 아니므로 컨트롤러의 모델에 직접 바인딩 할 수 없습니다.

    .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]);
                    });
                });
            }
        };
    }]);
    

    두 번째로 인스턴스 메소드 create를 사용하여 서버에서 create를 호출 할 때 데이터를 변형 할 수있는 myObject라는 팩토리를 만들었습니다. 이 메서드는 FormData 객체에 모든 것을 추가하고 transformRequest 메서드 (angular.identity)를 사용하여 변환합니다. 헤더를 undefined로 설정하는 것이 중요합니다. (이전 Angular 버전은 undefined보다 설정해야합니다.) 이렇게하면 다중 데이터 / 경계 표식이 자동으로 설정됩니다 (Carlos의 게시물 참조).

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

    클라이언트 측에서해야 할 일은 myController에서 새로운 myObject를 인스턴스화하고 내 양식을 제출할 때 컨트롤러의 create 함수에서 create 메소드를 호출하는 것입니다.

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

    RestController에서 다음과 같이 간단히 할 수 있습니다 : (우리가 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
    }
    

    공지, 나는 requestparameters를 사용하지 않고 단지 봄이 JSON을 POJO / DTO 변환하도록한다. MultiPartResolver 빈을 올바르게 설정하고 pom.xml에 추가했는지 확인하십시오. (그리고 필요하다면 Jackson-Mapper)

    spring-context.xml

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

    Pom.hml

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

    4.이것을 시도 할 수 있습니다.

    이것을 시도 할 수 있습니다.

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

    .자바

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

    그것은 https://github.com/murygin/rest-document-archive를 기반으로합니다.

    파일 업로드의 좋은 예가 있습니다. https://murygin.wordpress.com/2014/10/13/rest-web-service-file-uploads-spring-boot/

  5. from https://stackoverflow.com/questions/21015891/spring-mvc-angularjs-file-upload-org-apache-commons-fileupload-fileuploade by cc-by-sa and MIT license