SpringMVC实现文件上传和下载

文件上传是项目开发中最常见的功能。为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。

Spring MVC 为文件上传提供了直接的支持,这种支持是用即插即用的 MultipartResolver 实现的。Spring MVC 使用 Apache Commons FileUpload技术实现了一个 MultipartResolver 实现类:CommonsMultipartResolver 。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。

 

一、简单文件上传和下载:

文件上传

1)pom.xml 导入依赖:

<!-- commons-fileupload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.3</version>
    </dependency>

2)在springmvc.xml进行配置文件如下:

     SpringMVC上下文中默认没有装配 MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置 MultipartResolver。

	<!-- 配置文件上传的解析器:-->
	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- 上传文件大小上限,单位为字节(10MB) -->
		<property name="maxInMemorySize">
			<value>10485760</value>
		</property>
		<!-- 上传文件大小上限,单位为字节(10MB) -->
		<property name="defaultEncoding" value="UTF-8"></property>
	</bean>

3)编辑上传文件的jsp页面:method="post" enctype="multipart/form-data"

	<div>
		<h4>文件上传</h4>
		<form action="<%=path %>/upload" method="post" enctype="multipart/form-data">
		文件说明:<input type="text" name="wjsm" /><br/>
		附件:<input type="file" name="file" /><br/>
		<input type="submit" value="提交" />
		</form>
	</div>

4)编写Controller以及处理方法:测试ok

	@RequestMapping(value = "/upload", method = RequestMethod.POST)
	public String upload(String wjsm, @RequestParam("file") MultipartFile file, HttpServletRequest request) throws Exception {
		System.out.println("文件说明:" + wjsm);
		if(!file.isEmpty()) {
			String path = request.getServletContext().getRealPath("/resource/uploadFile");
			//获取上传文件的原名
			String fileName = file.getOriginalFilename();
			File desfile = new File(path,fileName);
			if(!desfile.getParentFile().exists()) {
				desfile.getParentFile().mkdirs();
			}
			file.transferTo(new File(path + File.separator + fileName));
		}else {
			System.out.println("文件为空!");
		}
		return "success";
	}

文件下载

文件下载比较简单,可直接在页面给出一个超链接,该链接 href 属性为要下载文件的文件,download 属性可自定义修改下载的文件名。这样就实现文件下载了。

	<a href="resource\uploadFile\t01cebc3fe2b30885fe.jpg" download="">下载</a>

但是如果该文件的文件名为中文文件名,在某些早起的浏览器上就会导致下载失败;如果使用最新的Firefox、Chrome、Opera、Safari则都可以正常下载文件名为中文的文件了。

SpringMVC提供了一个 ResponseEntity 类型,使用它可以很方便地定义返回的 HttpHeaders 和HttpStatus 。以下代码演示文件的下载功能:

	@RequestMapping(value="/download")
	public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam("fileName") String fileName) throws Exception{
		//下载文件路径
		String path = request.getServletContext().getRealPath("/resource/uploadFile");
		File file = new File(path + File.separator + fileName);

		HttpHeaders headers = new HttpHeaders();
		//下载显示的文件名,并解决中文名称乱码问题
		String downloadFileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1");
		//通知浏览器以attachment(下载方式)打开
		headers.setContentDispositionFormData("attachment", downloadFileName);
		//applicatin/octet-stream: 二进制流数据(最常见的文件下载)
		headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

		return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
	}

另外需要注意的一点:ajax请求无法响应下载功能,我们可构建表单进行提交,下面有使用到。

 

二、实际项目件上传和下载:

 数据表只保存路径文件和文件名, 上传与下载的文件都存放到约定好的本地磁盘的某个路径下。

tomcat 默认访问运行项目下的资源文件, 无法访问本地磁盘的文件,可通过在 server.xml 配置tomcat虚拟路径来读取。

	  <!-- 增加的-path="/虚拟名" docBase="虚拟路径" -->
	  <Context docBase="D:\JQ\JQuploadfile\attachments\" path="/DJQ" reloadable="true"/> 

 页面这样访问即可

附件:<a href="/DJQ/${user.fjpath }" download="">${user.fjm }</a> <br/>

件上传

1)model:忽略校验:

public class User {

	private int id;

	@NotEmpty(message="用户名不能为空")
	@Pattern(regexp="^\\w{6,18}$",message="用户名必须由6到18位的数字字母或者下划线组成")
	private String username;

	@NotNull
	@Length(min=6,max=32,message="密码必须为6到32位的字符串")
	private String password;

	@Past
	private Date birthday;

	@NotNull(message="年龄不能为空")
	@Max(value=100)
	@Min(value=1)
	private Integer age;

	@DecimalMax(value="2.5")
	@DecimalMin(value="1.0")
	private Double height;

	@Email
	private String email;
	@NotNull
	private Department department;
	private String fjm;	//附件原名
	private String fjpath; // 附近路径

... }

2)新增页面(表单添加附件上传功能):

	<h3>新增用户</h3>
	<form:form action="${pageContext.request.contextPath }/add" method="post" enctype="multipart/form-data" modelAttribute="user" >
		用户名:<form:input path="username"/>  <form:errors path="username"/> <br/>
		用户密码:<form:input path="password"/> <form:errors path="password"/>  <br/>
		email:<form:input path="email"/>  <form:errors path="email"/><br/>
		用户所属部门:<form:select path="department.id" itemValue="id" itemLabel="departmentname" items="${departments }"/>  <br/>
		生日:<form:input path="birthday"/>  <form:errors path="birthday"/><br/>
		年龄:<form:input path="age"/>   <form:errors path="age"/><br/>
		身高:<form:input path="height"/> <form:errors path="height"/><br/>
		附件:<input type="file" name="file" /><br/>
		<input type="submit" value="保存"> <br/>
	</form:form>

3)controller 处理方法: 忽略校验, 实测ok

	@RequestMapping(value="/add", method = RequestMethod.POST)
	public String addfj(@Valid User user, BindingResult errors, Model model, @RequestParam("file") MultipartFile file, HttpServletRequest request) throws Exception {
		String realpath = "D:\\JQ\\JQuploadfile\\attachments\\";
		String savepath = "t_user";
		String uploadfilepath = realpath + savepath;

		//判断数据绑定结果 errors.getErrorCount()>0 也可以
		if (errors.hasFieldErrors()) {
			System.out.println("参数值不合法!");
			// 获得所有的属性错误
			List<FieldError> fieldErrors = errors.getFieldErrors();
			for (FieldError fieldError : fieldErrors) {
				// 把错误信息存放在model对象中
				model.addAttribute(fieldError.getField(), fieldError.getDefaultMessage());
				System.out.println(fieldError.getField() + "===" + fieldError.getDefaultMessage());
				model.addAttribute("departments", departmentService.getAllList());
				return "user-add";
			}
		}

		if(!file.isEmpty()) {
			//获取上传文件的原名
			String fileName = file.getOriginalFilename();
			File desfile = new File(uploadfilepath,fileName);
			if(!desfile.getParentFile().exists()) {
				desfile.getParentFile().mkdirs();
			}
			file.transferTo(desfile);
			user.setFjm(fileName);
			user.setFjpath(savepath + File.separator + fileName); //保存文件路径
			System.out.println(user);
		}else {
			System.out.println("文件为空!");
		}

		userService.add(user);
		return "redirect: /sshweb/users";
	}

文件下载

 查看详情页面提供下载功能 :

1、超链接下载:简单方便

附件:<a href="/DJQ/${user.fjpath }" download="">${user.fjm }</a> <br/>

2、SpringMVC下载:可构建表单进行提交

ajax请求无法响应下载功能因为response原因,一般请求浏览器是会处理服务器输出的response,例如生成png、文件下载等,然而ajax请求只是个“字符型”的请求,即请求的内容是以文本类型存放的。文件的下载是以二进制形式进行的,虽然可以读取到返回的response,但只是读取而已,是无法执行的,说白点就是js无法调用到浏览器的下载处理机制和程序。

有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了。下表中列出了一些URL特殊符号及编码

解决的方法:

replace() 方法如果直接用str.replace("-","!")只会替换第一个匹配的字符.
而str.replace(/\-/g,"!")则可以替换掉全部匹配的字符(g为全局标志)。
replace()

js中替换字符变量如下:

 data2=data2.replace(/\%/g,"%25");
 data2=data2.replace(/\#/g,"%23");
 data2=data2.replace(/\\{1}/g,"%2F");

代码中可以使用

URLEncoder.encode(url, "UTF-8");

1)jsp页面:

	<h3>查看用户信息</h3>
	<form id="userShowForm" action="" method="post" enctype="multipart/form-data">
		用户编号:<input type="text" name="id" value="${user.id }"/> <br/>
		用户名:<input type="text" name="username" value="${user.username }"/> <br/>
		用户密码:<input type="text" name="password" value="${user.password }"/> <br/>
		email:<input type="text" name="email" value="${user.email }"/><br/>
		用户所属部门:<input type="text" name="email" value="${user.department.departmentname }"/><br/>
		生日:<input type="text" name="birthday" value="${user.birthday }"/>  <br/>
		年龄:<input type="text" name="age" value="${user.age }"/>  <br/>
		身高:<input type="text" name="height" value="${user.height }"/>  <br/>
<%-- 		附件:<a href="/DJQ/${user.fjpath }" download="">${user.fjm }</a> <br/> --%>
		附件:<a href="javascript: void(0)" id="downloadfj" url="<%=path%>/download?fjm=${user.fjm}" fjpath="${user.fjpath }">${user.fjm }</a> <br/>
	</form>
<script type="text/javascript" src="${pageContext.request.contextPath }/static/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
	$("#downloadfj").on("click",function(){
		var _fjpath = $(this).attr("fjpath").replace(/\\{1}/g,"%2F");
		var _url = $(this).attr("url") + "&fjpath=" + _fjpath;
		$("#userShowForm").attr("action",_url).submit();
	});
</script>

2)controller 处理方法: 实测ok

	@RequestMapping(value="/download")
	public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam("fjm") String fjm, @RequestParam("fjpath") String fjpath) throws Exception{
		String realpath = "D:\\JQ\\JQuploadfile\\attachments\\";
		//下载的文件
		String downloadpathfile = realpath + fjpath;
		File file = new File(downloadpathfile);

		HttpHeaders headers = new HttpHeaders();
		//下载显示的文件名,并解决中文名称乱码问题
		String downloadFileName = new String(fjm.getBytes("UTF-8"),"iso-8859-1");
		//通知浏览器以attachment(下载方式)打开
		headers.setContentDispositionFormData("attachment", downloadFileName);
		//applicatin/octet-stream: 二进制流数据(最常见的文件下载)
		headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

		return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
	}

数据库信息:

SpringMVC实现文件上传和下载-LMLPHP

SpringMVC实现文件上传和下载-LMLPHP

 

 

10-03 21:53