文件上传要求form表单的请求方式必须为post,并且添加属性enctype="multipart/form-data"
SpringMVC中将上传的文件封装到MultipartFile
对象中,通过此对象可以获取文件相关信息。
SpringMVC为文件上传提供了直接支持,这种支持是通过即插即用的MultipartResolver
实现的。Spring用Jakarta Commons FileUpload
技术实现了一个MultipartResolver
实现类:CommonsMultipartResolver
。
SpringMVC上下文中默认没有装配MultipartResolver
,因此默认情况下不能处理文件上传工作,如果想使用Spring的文件上传功能,需在上下文中配置MultipartResolver
。
【1】CommonsMultipartResolver配置
① CommonsMultipartResolver
Apache Commons FileUpload 1.2
或更高版本的基于Servlet
的 MultipartResolver
实现。提供了继承于CommonsFileUploadSupport
的maxUploadSize、maxInMemorySize
以及defaultEncoding
属性。有关默认值和接受值的详细信息,请参阅相应的ServletFileUpload
/DiskFileItemFactory
属性(“sizeMax
”、“sizeThreshold
”、“headerEncoding
”)。
其会将临时文件保存到servlet容器的临时目录。需要通过应用程序上下文或通过接受ServletContext(用于独立使用)的构造函数初始化实例。
实现类如下所示,继承了CommonsFileUploadSupport
并实现了MultipartResolver
和ServletContextAware
接口。
② 主要方法
① 有参构造方法
获取servletContext实例进行CommonsMultipartResolver实例化
public CommonsMultipartResolver(ServletContext servletContext) {
this();
setServletContext(servletContext);
}
② 获取ServletFileUpload
protected FileUpload newFileUpload(FileItemFactory fileItemFactory) {
return new ServletFileUpload(fileItemFactory);
}
③ 设置ServletContext 引用
@Override
public void setServletContext(ServletContext servletContext) {
if (!isUploadTempDirSpecified()) {
getFileItemFactory().setRepository(WebUtils.getTempDir(servletContext));
}
}
④ 检测是否Multipart请求
@Override
public boolean isMultipart(HttpServletRequest request) {
return ServletFileUpload.isMultipartContent(request);
}
⑤ 核心方法-将请求包装为MultipartHttpServletRequest
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
Assert.notNull(request, "Request must not be null");
if (this.resolveLazily) {
return new DefaultMultipartHttpServletRequest(request) {
protected void initializeMultipart() {
MultipartParsingResult parsingResult = parseRequest(request);
setMultipartFiles(parsingResult.getMultipartFiles());
setMultipartParameters(parsingResult.getMultipartParameters());
setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
}
};
}
else {
MultipartParsingResult parsingResult = parseRequest(request);
return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
}
}
⑥ 核心方法-解析请求
protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
String encoding = determineEncoding(request);
FileUpload fileUpload = prepareFileUpload(encoding);
try {
List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
return parseFileItems(fileItems, encoding);
}
catch (FileUploadBase.SizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
}
catch (FileUploadBase.FileSizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex);
}
catch (FileUploadException ex) {
throw new MultipartException("Failed to parse multipart servlet request", ex);
}
}
⑦ 清理fileItems内容
public void cleanupMultipart(MultipartHttpServletRequest request) {
if (!(request instanceof AbstractMultipartHttpServletRequest) ||
((AbstractMultipartHttpServletRequest) request).isResolved()) {
try {
cleanupFileItems(request.getMultiFileMap());
}
catch (Throwable ex) {
logger.warn("Failed to perform multipart cleanup for servlet request", ex);
}
}
}
底层是对 org.apache.commons.fileupload
下面的几个类做了封装,可以参考博文:文件上传 - Java原生实现
③ 加入SpringMVC依赖的Java原生的jar
如下图所示,使用Java原生进行文件上传需要的jar包
可以使用maven依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
④ 配置CommonsMultipartResolver
spring的xml文件配置如下
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--需与jsp页面编码保持一致-->
<property name="defaultEncoding" value="UTF-8"></property>
<!--限制上传大小-->
<property name="maxUploadSize" value="102400000"></property>
</bean>
其他属性如下图所示:
属性解释如下:
resolveLazily:延迟解析,默认为false--立即解析multipart request;
defaultEncoding:解析请求的默认字符编码 ; 默认值为"ISO-8859-1"。通常设置为"UTF-8";
maxUploadSize:文件上传最大值; 默认值为 -1(表示没有限制);
maxUploadSizePerFile:每个文件上传最大值;默认值为 -1(表示没有限制);
maxInMemorySize:存储在内存的最大值;默认值为10240B(10KB);
uploadTempDir:上传文件的临时目录;默认值为WEB应用程序的临时目录;
servletContext:the servletContext to use;
【2】测试代码
① form表单
<form action="testFileUpload" method="POST" enctype="multipart/form-data">
File: <input type="file" name="file"/>
Desc: <input type="text" name="desc"/>
<input type="submit" value="Submit"/>
</form>
② 后台代码
"/testFileUpload")(
public String testFileUpload( ("desc") String desc,
("file") MultipartFile file) throws IOException{
if (!file.isEmpty()) {
System.out.println("desc: " + desc);
System.out.println("OriginalFilename(原始文件名字): " + file.getOriginalFilename());
System.out.println("InputStream(获取的文件输入流): " + file.getInputStream());
System.out.println("文件大小为(单位为字节Byte): " + file.getSize());
System.out.println("文件内容类型为: " + file.getContentType());
file.transferTo(new File("D:\\temDirectory\\"+file.getOriginalFilename()));
}
return "success";
}
如上述代码所示,可以拿到文件名与输入流以及文件大小。最后一个方法file.transferTo
很有意思,可以直接保存到目标路径下的文件:
void org.springframework.web.multipart.MultipartFile.transferTo(File dest)
throws IOException, IllegalStateException
- 将接收到的文件传输到给定的目标文件。
- 这可能会移动文件系统中的文件,复制文件系统中的文件,或将内存保留的内容保存到目标文件。
- 如果目标文件已存在,将首先删除它。
- 如果文件已在文件系统中移动,则无法再次调用此操作。
- 因此,只需调用此方法一次,即可使用任何存储机制。
Chrome F12追踪网络展示如下:
③ 上传多个文件
表单内容:多个文件域(上传单文件时注释掉)
<form action="face/receiveImg" method="POST" enctype="multipart/form-data">
File: <input type="file" name="file"/>
Desc: <input type="text" name="desc"/>
<!-- File2: <input type="file" name="file"/> -->
<!-- Desc2: <input type="text" name="desc2"/> -->
<input type="submit" value="Submit"/>
</form>
也可以使用multiple 属性(上传单文件时去掉该属性)
<form action="face/receiveImg" method="POST" enctype="multipart/form-data">
File: <input type="file" name="file" multiple="multiple"/>
Desc: <input type="text" name="desc"/>
<input type="submit" value="Submit"/>
</form>
上传单文件时,后台使用MultipartFile file,MultipartFile[] file,@RequestParam("file")MultipartFile file,@RequestParam MultipartFile file
均可以正常接收(这里name默认为file哦)。
上传多文件时,后台只有使用 @RequestParam("file") MultipartFile[] file
才可以正常接收。
【3】使用FormData上传多个文件
通过FormData
对象可以组装一组用 XMLHttpRequest
发送请求的键/值对。它可以更灵活方便的发送表单数据,因为可以独立于表单使用。
如果你把表单的编码类型设置为multipart/form-data
,则通过FormData
传输的数据格式和表单通过submit()
方法传输的数据格式相同
即,此时使用FormData对象传送的数据不会发送格式改变
① 表单示例
这里使用两个文本域来上传两个Excel表格:
<form id="myform" name="myform" action="<%=basePath%>data/saveDataImport.do" class="layui-form" method="post" enctype="multipart/form-data" >
<div class="layui-form-item">
<label class="layui-form-label">请选择POS表:</label>
<div class="layui-input-block">
<input id="pos" type="file" name="pos" >
</div>
</div>
<div class="layui-form-item" >
<label class="layui-form-label">请选择商品表:</label>
<div class="layui-input-block">
<input id="goods" type="file" name="goods" >
</div>
</div>
<div class="layui-form-item" style="margin-top: 40px;">
<div class="layui-input-block">
<button id = "upload" class="layui-btn" type="button">确定</button>
<button name="cancel" type="button" class="layui-btn" onclick="f_cancel();">取消</button>
</div>
</div>
</form>
② 构建FormData对象
下面两种正确方式:
var posFormData = new FormData($("#myform")[0]);
or
var posFormData = new FormData($("form")[0]);
参数对比如下图:
FormData对象
③ ajax提交
$(function(){
$("#upload").click(function(){
var posFormData = new FormData($("#myform")[0]);
$.ajax({
url: url,
type: 'POST',
data: posFormData,
contentType: false, //禁止设置请求类型
processData: false, //禁止jquery对DAta数据的处理,默认会处理
beforeSend: function(){
//返回的参数item,即为当前的input DOM对象
index = layer.load(1,{shade: [0.3,'grey']});
},
success: function(data) {
if (typeof data != "object") {
jsonReturn = eval("("+data+")");
}else{
jsonReturn=data;
}
//关闭遮罩层
layer.close(index);
if(jsonReturn.code == "true"){
layer.msg(jsonReturn.message,{icon:1,time: 1000},function(){
f_cancel();
top.f_getframe("goods_index_do").f_goods_sale_query();
});
}else{
layer.msg(jsonReturn.message,{icon: 7,time: 2000});
}
}
});
});
});
上传参数如下图:
使用submit方式上传文件参数如下图:
对比发现,二者数据格式完全一致!
④ 后台接收
value="saveDataImport",produces="application/json;charset=utf-8" )(
public String saveDataImport( (value="pos",required=false)MultipartFile posFile, (value="goods",required=false)MultipartFile goodsFile){
//...
}