SpringMVC使用MultipartResolver和MultipartFile实现文件上传

文件上传要求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或更高版本的基于ServletMultipartResolver实现。提供了继承于CommonsFileUploadSupport的maxUploadSize、maxInMemorySize以及defaultEncoding属性。有关默认值和接受值的详细信息,请参阅相应的ServletFileUpload/DiskFileItemFactory属性(“sizeMax”、“sizeThreshold”、“headerEncoding”)。

其会将临时文件保存到servlet容器的临时目录。需要通过应用程序上下文或通过接受ServletContext(用于独立使用)的构造函数初始化实例。

实现类如下所示,继承了CommonsFileUploadSupport并实现了MultipartResolverServletContextAware接口。
在这里插入图片描述

② 主要方法

① 有参构造方法
获取servletContext实例进行CommonsMultipartResolver实例化

public CommonsMultipartResolver(ServletContext servletContext) {
		this();
		setServletContext(servletContext);
}

② 获取ServletFileUpload

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

@Override
	public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
		Assert.notNull(request, "Request must not be null");
		if (this.resolveLazily) {
			return new DefaultMultipartHttpServletRequest(request) {
				@Override
				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>

② 后台代码

@RequestMapping("/testFileUpload")
public String testFileUpload(@RequestParam("desc") String desc, 
		@RequestParam("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方式上传文件参数如下图:

这里写图片描述

对比发现,二者数据格式完全一致!

④ 后台接收

@RequestMapping(value="saveDataImport",produces="application/json;charset=utf-8" )
@ResponseBody
public String saveDataImport(@RequestParam(value="pos",required=false)MultipartFile posFile,@RequestParam(value="goods",required=false)MultipartFile goodsFile){

//...
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值