7.1 文件上传

7.1 文件上传

文件上传是项目开发当中最常用的功能。为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这种情况下,浏览器才会把用户选择的文件二进制数据发送给服务器
一旦将enctype设置为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。2003年,Apache Software Foundation发布了开源的Commons File Upload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。
Servlet3.0规范的HttpServletRequest已经提供了方法来处理文件上传,但这种上传需要在Servlet中完成。而Spring MVC则提供了更简单的封装。
Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。SpringMVC使用ApacheCommonsFileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons File Upload的组件
Apache Commons File Upload的组件共有两个,最新版本分别是commons-fileupload-1.3.3.jarcommons-io-2.6.jar
commons-fileupload-1.3.3.jar的官网下载地址为:http://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi
commons-io-2.6.jar的官网下载地址为:http://commons.apache.org/proper/commons-io/download_io.cgi

示例 Spring MVC的文件上传

Spring配置文件中添加配置

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

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 文件上传配置 -->
<bean
id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传文件大小上限,单位为字节(10MB) -->
<property name="maxUploadSize">
<value>10485760</value>
</property>
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding">
<value>UTF-8</value>
</property>
</bean>

引入CommonsFileUpload的jar包

需要注意的是,CommonsMultipartResolver必须依赖于ApacheCommonsFileUpload的组件,所以需要将ApacheCommonsFileUploadjar包放到项目的类路径下。

MultipartFile对象

Spring MVC会将上传文件绑定到MultipartFile对象。MultipartFile提供了获取上传文件内容、文件名等方法。通过transferTo()方法可以将文件上传。MultipartFile对象中的常用方法如下:

方法 描述
byte[] getBytes() 获取文件数据
String getContentType() 获取文件MIME类型,如image/jpeg
InputStream getInputStream() 获取文件流
String getName() 获取表单中文件组件的名称
Stringg etOriginalFilename() 获取上传文件的原名
long getSize() 获取文件的字节大小,单位为byte
boolean isEmpty() 是否有上传的文件
void transferTo(Filedest) 将上传文件保存到一个目标文件中。

error.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试文件上传</title>
</head>
<body>
<strong>上传文件失败!</strong>
</body>
</html>

success.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试文件上传</title>
</head>
<body>
<strong>上传文件成功!</strong>
</body>
</html>

uploadForm.jsp

负责上传文件的表单和一般表单有一些区别,**负责上传文件的表单的编码类型(enctype)必须是”multipart/form-data“**。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上传</title>
</head>
<body>
<h2>文件上传</h2>
<form action="upload" enctype="multipart/form-data" method="post">
<table>
<tr>
<td>文件描述:</td>
<td><input type="text" name="description"></td>
</tr>
<tr>
<td>请选择文件:</td>
<td><input type="file" name="file"></td>
</tr>
<tr>
<td><input type="submit" value="上传"></td>
</tr>
</table>
</form>
</body>
</html>

测试链接

1
<a href="uploadForm">Spring MVC的文件上传功能</a>

上传文件

输入文件描述信息并选择上传文件,如下图所示:
这里有一张图片
单击”上传“按钮,文件会被上传并保存到服务器部署项目的images文件夹下面。

上传效果

这里有一张图片

控制台输出

1
2
使用Spring MVC上传
上传文件路径:E:\apache-tomcat-8.5.35\webapps\FileUploadTest\images\2-2.jpg

进入该目录可以看到上传后的图片:
这里有一张图片

示例 使用对象接收上传文件

在实际的项目开发中,很多时候上传的文件会作为对象的属性被保存。Spring MVC的处理也非常简单。

域对象

上传的图片将作为User对象的属性,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 域对象,实现序列化接口
public class User
implements Serializable
{
private static final long serialVersionUID = 1L;
private String username;
// 对应上传的file,类型为MultipartFile,上传文件会自动绑定到image属性当中
private MultipartFile image;
public User()
{
super();
}
// 此处省略getter和setter方法,请自己补上
}

测试链接

1
<a href="registerForm">用户头像上传功能</a>

请求处理方法

1
2
3
4
5
6
@GetMapping(value = "/registerForm")
public String registerForm()
{
// 跳转到文件上传页面
return "registerForm";
}

registerForm.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户注册</title>
</head>
<body>
<h2>用户注册</h2>
<form action="register" enctype="multipart/form-data" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>请上传头像:</td>
<td><input type="file" name="image"></td>
</tr>
<tr>
<td><input type="submit" value="注册"></td>
</tr>
</table>
</form>
</body>
</html>

设置用户名和头像

这里有一张图片

注册请求处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@PostMapping(value = "/register")
// @ModelAttribute User user将整个表单数据绑定到User对象中
public String register(HttpServletRequest request, @ModelAttribute
User user, Model model) throws Exception
{
System.out.println(user.getUsername());
// 如果文件不为空,写入上传路径
if (!user.getImage().isEmpty())
{
// 设置要上传到服务器的目录
String path = request.getServletContext().getRealPath("/images");
// 获取上传的文件的文件名
String filename = user.getImage().getOriginalFilename();
// 在该目录下创建同名文件
File filepath = new File(path, filename);
// 判断路径是否存在,如果不存在就创建一个
if (!filepath.getParentFile().exists())
{
filepath.getParentFile().mkdirs();
}
// 将用户上传的图片保存到服务器目录中
user.getImage().transferTo(filepath);
// 将用户添加到model
model.addAttribute("filename",
user.getImage().getOriginalFilename());
System.out.println("上传文件路径:" + filepath.getAbsolutePath());
return "userInfo";
} else
{
return "error";
}
}

userInfo.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件下载</title>
</head>
<body>
<h3>文件下载</h3>
<br>
<a
href="javascript:window.location.href = 'download?filename=' + encodeURIComponent( '${requestScope.filename }' )">
${requestScope.filename } </a>
</body>
</html>

点击链接,download请求处理方法将会被调用,实现下载图片.
这里有一张图片

示例 SpringMVC的文件下载

download请求处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@GetMapping(value = "/download")
public ResponseEntity<byte[]> download(HttpServletRequest request,
@RequestParam("filename")
String filename,
@RequestHeader("User-Agent")
String userAgent) throws Exception
{
// 下载文件路径
String path = request.getServletContext().getRealPath("/images");
// 构建File
File file = new File(path + File.separator + filename);
// ok表示Http协议中的状态 200
BodyBuilder builder = ResponseEntity.ok();
// 内容长度
builder.contentLength(file.length());
// application/octet-stream : 二进制流数据(最常见的文件下载)。
builder.contentType(MediaType.APPLICATION_OCTET_STREAM);
// 使用URLDecoder.decode对文件名进行解码
filename = URLEncoder.encode(filename, "UTF-8");
// 设置实际的响应文件名,告诉浏览器文件要用于【下载】、【保存】attachment 以附件形式
// 不同的浏览器,处理方式不同,要根据浏览器版本进行区别判断
if (userAgent.indexOf("MSIE") > 0)
{
// 如果是IE,只需要用UTF-8字符集进行URL编码即可
builder.header("Content-Disposition",
"attachment; filename=" + filename);
} else
{
// 而FireFox、Chrome等浏览器,则需要说明编码的字符集
// 注意filename后面有个*号,在UTF-8后面有两个单引号!
builder.header("Content-Disposition",
"attachment; filename*=UTF-8''" + filename);
}
return builder.body(FileUtils.readFileToByteArray(file));
}

下载效果

这里有一张图片
SpringMVC提供了一个ResponseEntity类型,使用它可以很方便地定义返回的BodyBuilderHttpHeadersHttpStatus
download处理方法接收到页面传递的文件名 filename后,使用 Apache CommonsFileUpload组件的 FileUtils读取项目的 images文件夹下的该文件,并将其构建成ResponseEntity对象返回客户端下载。
使用 ResponseEntity对象,可以很方便地定义返回的 BodyBuilderHttpHeadersHttpStatus,BodyBuilder对象用来构建返回的Body;类型代表的是Http协议中的头信息;
Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。有关BodyBuilderMediaTypeHttpStatus类的详细信息参考Spring MVCAPI文档。