Marco's Java【SpringMVC入门(六) 之 SpringMVC的文件上传和下载】

本文详细介绍了如何使用SpringMVC实现文件上传和下载功能,包括配置解析器、定义控制器处理请求、文件存储和下载过程。
前言

在这个互联网已经普及的社会,文件的上传是必不可的了,可以说,只要你用过电子产品,那就必然接触并上传过文件,那不然你玩微信啊,陌陌啥的,不上传个帅一点的图片,多发几个高大上的朋友圈,也聊不到妹子啊…
哈哈,开玩笑的,毕竟撩妹子这种事情,靠这些花里胡哨的东西可是行不通的,得靠脸… 还得有钱…

切入正题,我们今天要讲得内容就是如何使用SpringMVC来实现文件得上传和下载

SpringMVC的文件上传

那么此次我们还是通过一个小案例来给大家演示文件上传及下载啦,首先我们先看文件上传的步骤
第一步:导包
在这里插入图片描述

第二步:创建一个简单得文件上传页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>
	<a href="../marco/attachment/queryAllAttachment.action">查询所有文件列表</a>
</h1>
	<h1>文件上传</h1>
<hr>
<form action="attachment/addAttachment.action" method="post" enctype="multipart/form-data">
	请选择文件:<input type="file" name="multiFile"><br>
	请选择文件:<input type="file" name="multiFile"><br>
	请选择文件:<input type="file" name="multiFile"><br>
	<input type="submit" value="提交">
</form>

<img alt="" src="../marco/attachment/downloadFile.action?id=13">
</body>
</html>

第三步:配置二进制流程解析
在学习SpringMVC之前,如果需要进行文件的传输,将文件转换为流之后成功被Controller控制端接收,需要在Controller上添加@MultipartConfig注解,通过getParts()方法获取流对象,并解析文件流的内容,非常复杂,而SpringMVC帮我们提供了一个内置的解析文件的类CommonsMultipartResolver,以下就是该类的xml配置

<!-- 实例化二进制流解析器 -->
 <bean id="multipartResolver" 
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 指定文件上传过程中提交的数据库的编码 -->
   <property name="defaultEncoding" value="UTF-8"></property>
    <!-- 指定上传文件的保存的临时目录 --> 
    <property name="uploadTempDir" value="/upload"></property>
    <!-- 指定上传的文件的最大大小 -->
    <property name="maxUploadSize" value="1024000000"></property>
 </bean>

需要注意的一点是,配置CommonsMultipartResolver的时候,必须要加上id=“multipartResolver”,否则文件上传不会生效,而且multipartResolver这个名字可不是乱取的哦,大家查看DispatcherServlet的时候可以找到CommonsMultipartResolver这个类,他的属性名就是multipartResolver,而DispatcherServlet正是通过这个名称搜索到这个类。
在这里插入图片描述

第四步:定义文件上传控制器处理上传请求
之前我们在页面上定义的input框中的name是multiFile,因此在接收文件数据源的时候名字也应该是multiFile,否则后台是接受不到文件流的。Attachement是我单独定义的一个类,主要用于存储文件的信息(包含文件名,文件大小,以及文件存放的路径) 到后台,为了突出本章节的重点,这里的JDBC操作的代码我就不放出来啦~

@Controller
@RequestMapping("attachment")
public class FileController {

	@Autowired
	private AttachementService attachementService;

	/**
	 * 文件上传
	 * @throws IOException 
	 * @throws IllegalStateException 
	 */
	@RequestMapping("addAttachment")
	public String addAttachment(MultipartFile[] multiFile) throws Exception {
		// 文件上传的父目录
		String parentPath = AppFileUtils.PATH;
		// 得到当前日期作为文件夹名称
		String dirName = RandomUtils.getCurrentDateForString();
		// 构造文件夹对象
		File dirFile = new File(parentPath, dirName);
		if (!dirFile.exists()) {
			dirFile.mkdirs();// 创建文件夹
		}
		for (MultipartFile multipartFile : multiFile) {
			// 得到文件原名
			String oldName = multipartFile.getOriginalFilename();
			//得到文件类型
			String contenttype=multipartFile.getContentType();
			//得到文件大小
			Double size = (double) multipartFile.getSize();
			// 根据文件原名得到新名
			String newName = RandomUtils.createFileNameUseTime(oldName);
			File dest = new File(dirFile, newName);
			multipartFile.transferTo(dest);
			
			AttachementVo attachementVo=new AttachementVo();
			attachementVo.setCreatetime(new Date());
			attachementVo.setOldname(oldName);
			attachementVo.setContenttype(contenttype);
			attachementVo.setSize(size);
			//路径为文件夹的名称+新的名称
			attachementVo.setPath(dirName+"/"+newName);
			//保存数据到数据库
			attachementService.addAttachment(attachementVo);
		}
		return "success.jsp";
	}
}

另外AppFileUtils和RandomUtils是我单独定义的两个工具类,RandomUtils是为了重新组装文件的名称,比较简单,网上一搜一大把

public class RandomUtils {
	
	private static SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd");
	private static SimpleDateFormat sdf2=new SimpleDateFormat("yyyyMMddHHmmssSSS");
	private static Random random=new Random();	
	/**
	 * 得到当前日期
	 */
	public static String getCurrentDateForString() {
		return sdf1.format(new Date());
	}	
	/**
	 * 生成文件名使用时间+4位随机数
	 * @param suffix 文件名称
	 */
	public static String createFileNameUseTime(String fileName) {
		String fileSuffix=fileName.substring(fileName.lastIndexOf("."),fileName.length());
		String time=sdf2.format(new Date());
		Integer num=random.nextInt(9000)+1000;
		return time+num+fileSuffix;
	}	
	/**
	 * 生成文件名使用UUID
	 * @param suffix 文件名称
	 */
	public static String createFileNameUseUUID(String fileName) {
		String fileSuffix=fileName.substring(fileName.lastIndexOf("."),fileName.length());
		return UUID.randomUUID().toString().replace("-", "").toUpperCase()+fileSuffix;
	}
}

AppFileUtil是为了方便获取文件存储的路径,我这里设置的路径是F:\upload,大家可能在这里会有疑问,因为这一点和我们之前在Servlet项目中讲到的不太一样,我们之前是将文件存储在webapps目录下,打个比方,我们在webapps下新建一个目录为userImg,并且将文件存储在userImg中,那么我们的网络访问路径就是127.0.0.1:8080/userImg/avatar.jpg,通过网络地址是可以直接访问到这张图片的。

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class AppFileUtils {
	
	/**
	 * 得到文件上传的路径
	 */
	public static String PATH;
	static {
		InputStream stream = AppFileUtils.class.getClassLoader().getResourceAsStream("file.properties");
		Properties properties=new Properties();
		try {
			properties.load(stream);
			PATH=properties.getProperty("path");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

但是实际开发中我们存储的不仅仅是图片,可能是文件,我们如果需要将文件下载下来的话,那么这种方式就行不通啦,而且还变相的存在着安全隐患,因为它直接的暴露了我们服务器和具体文件夹的信息。
因此综合考虑,我这里将存储的路径改到了其他的盘符,那么问题又来了,如果说将文件存储在webapps下我们还可以通过网络地址访问到,那我放在其他盘符下怎么访问的到呢?那就跟着我往下走啦~

第五步:文件下载
这里就是我们的重点啦,我们之前将文件的路径存在了Attachement对象中,那么当页面发出请求,给到所需要下载或者浏览的文件的id,就可以获取到文件的子路径(比方说全路径是 F:\upload\2019-07-24\xxxx.JPG,那么子路径就是upload\2019-07-24\xxxx.JPG),通过这种方式存储的话避免了我们文件夹被暴露的风险。

那当我们拿到了文件的子路径之后,就可以拼接文件的全路径啦。

/**
* 下载的方法
*/
@RequestMapping("downloadFile")
public ResponseEntity<Object> downloadFile(AttachementVo attachementVo,HttpServletResponse response){
   //1,根据ID查询文件
   Attachment attachment=this.attachementService.queryAttachmentById(attachementVo.getId());
   //2,得到文件的相对路径
   String path=attachment.getPath();
   //3,拿到文件的老名字
   String oldName=attachment.getOldname();
   return AppFileUtils.downloadFile(response, path, oldName);
}

拿到全路径之后,我这里封装了一个downloadFile的方法,通过FileUtils.readFileToByteArray(file)可以获取到文件的流对象,然后封装为ResponseEntity对象后,页面通过一个a标签就可以下载这个流对象也就是相对应的文件或者图片啦
<a href="downloadFile.action?id=1">

/**
* 文件下载
 * @param response
 * @param path
 * @param oldName
 * @return
 */
public static  ResponseEntity<Object> downloadFile(HttpServletResponse response, String path, String oldName) {
	//4,使用绝对路径+相对路径去找到文件对象
	File file=new File(AppFileUtils.PATH,path);
	//5,判断文件是否存在
	if(file.exists()) {
		try {
			try {
				//如果名字有中文 要处理编码
				oldName=URLEncoder.encode(oldName, "UTF-8");
			} catch (Exception e) {
				e.printStackTrace();
			}
			//把file转成一个bytes
			byte [] bytes=FileUtils.readFileToByteArray(file);
			HttpHeaders header=new HttpHeaders();
			//封装响应内容类型(APPLICATION_OCTET_STREAM 响应的内容不限定)
			header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
			//设置下载的文件的名称
			header.setContentDispositionFormData("attachment", oldName);
			//创建ResponseEntity对象
			ResponseEntity<Object> entity=
					new ResponseEntity<Object>(bytes, header, HttpStatus.CREATED);
			return entity;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}else {
		PrintWriter out;
		try {
			out = response.getWriter();
			out.write("文件不存在");
			out.flush();
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
}

那同理,如果我们保存的是一张图片的话,通过一个img标签就可以访问到相对应的图片啦
<img alt="" src="../marco/attachment/downloadFile.action?id=13">
大家赶紧试试吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值