文章目录
6. SpringMVC实现文件上传下载
6.1. 文件上传细节分析
6.1.1. 文件上传的3要素
form表单的 enctype取值必须是:multipart/form-data
(默认值是:application/x-www-form-urlencoded)
enctype:是表单请求正文的类型
method属性取值必须是 Post
提供一个文件选择域
HTTP协议中新增的rfc1867协议,为 http 协议添加了上传功能。
根据 rfc1867协议规范,enctype=“multipart/form-data”, method=post, type=“file” 。这三个属性是必须的。multipart/form-data 是新增的编码类型,以提高二进制文件的传输效率。
-
enctype属性值
enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。
值 | 描述 |
---|---|
application/x-www-form-urlencoded | 默认值。在发送前对所有字符进行编码为名称/值对 (将空格转换为 “+” 符号,特殊字符转换为 ASCII HEX 值)。 |
multipart/form-data | 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。 |
text/plain | 空格转换为 “+” 加号,但不对特殊字符编码。 |
6.1.2. 文件上传流程
- 客户端的浏览器,如 Microsoft IE, Mozila Firefox, Chrome 等,按照此规范将用户指定的文件发送到服务器端的网页程序。
- 服务器端的网页程序,如 php, asp, jsp 等,可以按照此规范,解析出用户发送来的文件。经过编码发送给服务器。
- http server ,例如 tomcat ,已经支持此协议,可接收发送来的文件。
6.1.4. 第三方工具
文件上传过程比较复杂,但方式是固定的,因此出现了很多第三方的 http upload file 工具库,我们使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:
- Commons-fileupload
- commons-io
commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始,它
工作时需要 commons-io 包的支持。
使用maven依赖导入commons-fileupload依赖即可,commons-io会自动导入
<!--文件上传-->
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
6.2. 文件上传
- 添加上传的jsp
- 配置文件上传解析器
- 编写controller完成上传
6.2.1. 上传的jsp页面
- upload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<form action="/upload.do" method="post" enctype="multipart/form-data">
名称:<input type="text" name="picname"/><br/>
图片:<input type="file" name="uploadFile"/><br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
6.2.2. 配置文件解析器
springmvc.xml添加文件上传的解析器
<!-- 配置文件上传解析器 -->
<!-- id 的值是固定的-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设定文件上传的最大值为5MB,5*1024*1024 -->
<property name="maxUploadSize" value="5242880"></property>
</bean>
6.2.3. controller完成文件上传
- 获取上传的文件名称
- 修改文件名,防止文件重名冲突
- 以日期区分保存文件的文件夹
- 将上传的文件保存到文件夹
//注意MultipartFile变量名必须和form表单的file的name属性一致
@RequestMapping("/upload")
public String uploadFile(String picname, MultipartFile uploadFile,
HttpServletRequest request) throws IOException {
if(uploadFile == null){
return "error";
}
//保存文件名
String fileName = "";
//1.获取原始文件名
String originalFilename = uploadFile.getOriginalFilename();
//文件扩展名
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
//把文件加上随机数,防止重名被覆盖
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
//2. 修改文件名,防止文件重名冲突
//文件名 = UUID_输入的文件名.扩展名 / UUID_原文件名.扩展名
//判断是否输入文件名
if(!StringUtils.isEmpty(picname)){
fileName = uuid+"_"+picname+suffix;
}else {
fileName = uuid+"_"+originalFilename;
}
System.out.println(fileName);
//3. 以日期区分保存文件的文件夹
//3.1.获取文件路径
ServletContext servletContext = request.getSession().getServletContext();
String basePath = servletContext.getRealPath("/upload");
//3.2.按日期创建文件夹,避免一个文件夹文件过多
String datePath = new SimpleDateFormat("yyyy-mm-dd").format(new Date());
//3.3.拼接存放文件的文件夹路径
File dir = new File(basePath + "/" + datePath);
//3.4.判断文件夹是否存在,不存在则创建目录
if (!dir.exists()){
dir.mkdir();
}
//4.将上传的文件保存到文件夹
//使用 MulitpartFile 接口中方法,把上传的文件写到指定位置
uploadFile.transferTo(new File(dir,fileName));
return "success";
}
- 图片被上传到服务器指定位置
6.3. 文件下载
文件下载步骤:
- 准备下载页面
- 实现文件下载方法
6.3.1. 下载页面
download.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件下载</title>
</head>
<body>
<a href="/download?filename=年轻的我.jpg">年轻的我</a>
</body>
</html>
6.3.2. 下载方法
@RequestMapping("/download")
public String download(String filename, HttpServletResponse response){
//没有下载的文件名可能是非法访问,直接返回null
if(StringUtils.isEmpty(filename)){
return null;
}
System.out.println("用户要下载的文件:"+filename);
//服务器存放的源文件
File sourceFile = new File("H:\\upload\\2020-07-27\\照片.jpg");
//数据写入到byte数组
byte[] bytes = null;
FileInputStream inputStream = null;
OutputStream outputStream = null;
try {
//将下载的文件通过输入流读取
inputStream = new FileInputStream(sourceFile);
//创建写出数据的字节数组
bytes = new byte[inputStream.available()];
//将输入流数据转成字节
inputStream.read(bytes);
//下载的响应头header中只支持ASCII,文件名需要进行编码,否则下载不显示文件名
filename = URLEncoder.encode(filename,"utf-8");
//设置文件下载的响应头
response.setHeader("Content-Disposition","attachment;filename="+filename);
outputStream = response.getOutputStream();
//将数据写出
outputStream.write(bytes);
//刷新缓冲区
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("文件下载异常!");
}finally {
//关闭输入流
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭输出流
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}