我有一个Java Spring MVC Web应用程序作为服务器。基于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发送文件和其他表单数据并将其放入服务器,那就太好了。
更新1
@Michael :我只能在控制台中看到这个,当我点击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
我的服务器在其他端口8080上运行。我在用约曼,嘟嘟声和弓箭。因此,thin 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
发布于 2014-02-28 16:43:13
我遇到了同样的问题,甚至在更新transformRequest之后也遇到了同样的问题。‘在某种程度上,标题边界似乎没有正确设置。
在http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs之后,问题就解决了。从该位置提取....
通过设置“Content-Type”:未定义,浏览器会为我们将Content-Type设置为multipart/form-data并填充正确的边界。手动设置‘Content-Type’:multipart/form-data无法填写请求的边界参数。
不确定这是否对任何人有帮助,但可能会让阅读这篇文章的人更容易……至少,这让它不那么困难了。
发布于 2015-02-04 00:07:43
引言
我也遇到过同样的问题,并找到了一个完整的解决方案,将json和文件从基于angular的页面发送到Spring MVC方法。
主要的问题是$http没有发送正确的内容类型头部(我将解释原因)。
关于多部分/表单数据的理论
为了同时发送json和file,我们需要发送一个multipart/form-data,这意味着“我们在正文中发送不同的项目,用一个特殊的分隔符分隔”。这个特殊的分隔符被称为“边界”,它是一个字符串,它不存在于要发送的任何元素中。
服务器需要知道正在使用哪个边界,因此必须在Content-type头部(Content-Type multipart/form-data;boundary=$the_boundary_used)中指明它。
所以..。需要两件事:
header中的
分隔每个请求参数
一个好的请求示例:
标题
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没有访问者,所以不可能知道正在使用哪个边界!
解决方案
您应该使用标准XMLHttpRequest,而不是在js中使用$http,如下所示:
//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;
}
发布于 2015-06-16 19:32:04
我的$http拦截器添加了authorization headers等内容,但卡洛斯·Verdes的回答失败了。因此我决定添加到他的解决方案中,并使用$http创建我的解决方案。
客户端Angular (1.3.15)
我的表单(使用controllerAs语法)假设有一个文件和一个包含我们需要发送到服务器的信息的简单对象。在本例中,我使用了一个简单的名称和类型字符串属性。
<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),这样我就可以访问它了。直接将其绑定到控制器中的模型将不起作用,因为输入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]);
});
});
}
};
}]);
其次,我使用一个实例方法create创建了一个名为myObject的工厂,它允许我在服务器上调用create时转换数据。此方法将所有内容添加到FormData对象,并使用transformRequest方法(angular.identity)对其进行转换。将标头设置为未定义的非常重要。(较早的Angular版本可能需要设置未定义的内容)。这将允许自动设置多数据/边界标记(参见卡洛斯的帖子)。
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
}
请注意,我没有使用requestparameters,而只是让spring进行JSON到POJO/DTO的转换。确保正确设置了MultiPartResolver bean并将其添加到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.xml
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
https://stackoverflow.com/questions/21015891
复制相似问题