Spring MVC - AngularJS - 文件上传 - org.apache.commons.fileupload.FileUploadException

 个信2502907653 发布于 2023-02-04 21:06

我有一个Java Spring MVC Web应用程序作为服务器.而基于AngularJS的应用程序作为客户端.

在AngularJS中,我必须上传文件并发送到服务器.

这是我的HTML

这是我的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 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.(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中,我已经提到了


        
    

我在用

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

怎么解决这个?

如果有人告诉我如何从angularJS发送文件和其他formdata并在服务器中获取它会很棒.

更新1

@Michael:当我点击提交时,我只能在控制台中看到这个.

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的其他端口运行.我是自耕农,咕噜咕噜和凉亭.所以瘦gruntfile.js我提到了服务器端口.所以它转到服务器并运行它并抛出异常

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

小智.. 22

我遇到了同样的问题,即使在更新transformRequest之后也遇到了同样的问题.'有些如何,标题边界似乎没有正确设置.

在http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs之后,问题得以解决.从位置提取....

通过设置'Content-Type':undefined,浏览器为我们设置Content-Type为multipart/form-data并填入正确的边界.手动设置'Content-Type':multipart/form-data将无法填写请求的边界参数.

不确定这是否有助于任何人,但也许让人们很容易看到这篇文章...至少,它使它变得不那么困难.

3 个回答
  • 介绍

    我遇到了同样的问题,并找到了一个完整的解决方案,可以将基于角度的页面的json和文件发送到Spring MVC方法.

    主要问题是$ http没有发送正确的Content-type标头(我将解释原因).

    关于多部分/表格数据的理论

    要发送json和文件,我们需要发送multipart/form-data,这意味着"我们在主体中发送由特殊分隔符分隔的不同项".这个特殊的分隔符叫做"boundary",它是一个字符串,不会出现在任何要发送的元素中.

    服务器需要知道正在使用哪个边界,因此必须在Content-type头中指示(Content-Type multipart/form-data; boundary = $ the_boundary_used).

    所以...需要两件事:

      在标题 - >指示multipart/form-data AND使用哪个边界(这里是$ http失败的地方)

      在body中 - >用边界分隔每个请求参数

    好请求的示例:

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

    我们发送2个参数的地方,"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;
    
    }
    

    2023-02-04 21:10 回答
  • 我遇到了同样的问题,即使在更新transformRequest之后也遇到了同样的问题.'有些如何,标题边界似乎没有正确设置.

    在http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs之后,问题得以解决.从位置提取....

    通过设置'Content-Type':undefined,浏览器为我们设置Content-Type为multipart/form-data并填入正确的边界.手动设置'Content-Type':multipart/form-data将无法填写请求的边界参数.

    不确定这是否有助于任何人,但也许让人们很容易看到这篇文章...至少,它使它变得不那么困难.

    2023-02-04 21:10 回答
  • Carlos Verdes的答案无法使用我的$ http拦截器,后者添加了授权标头等等.所以我决定添加他的解决方案并使用$ http创建我的.

    Clientside Angular(1.3.15)

    我的表单(使用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),以便我可以访问它.将其直接绑定到控制器中的模型将不起作用,因为input type = file不是内置功能.

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

    其次,我创建了一个名为myObject的工厂,它带有一个实例方法create,允许我在服务器上调用create时转换数据.此方法将所有内容添加到FormData对象,并使用transformRequest方法(angular.identity)将其转换.将标头设置为undefined至关重要.(较旧的Angular版本可能需要设置未定义的内容).这将允许自动设置多数据/边界标记(请参阅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);
    

    Serverside Spring(4.0)

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

    请注意,我没有使用请求参数,只是让spring执行JSON到POJO/DTO转换.确保您也正确设置了MultiPartResolver bean并将其添加到您的pom.xml中.(如果需要,还有Jackson-Mapper)

    弹簧的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>
    

    2023-02-04 21:13 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有