Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器

时间:2023-03-08 16:24:49

1、文件上传。

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

spring MVC上下文中默认没有装配MultipartResolver,因此默认情况下不具备文件上传功能。如果需要使用spring的文件上传功能,还需要在上下文配置MultipartResolver。

Spring MVC的form表单数据类型必须是multipart/form-data类型(二进制流方式),且必须是post方式。使用MultipartFile作为处理方法的参数接收form表单提交的file文件。MultipartFile提供的方法有:

Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器

spring MVC上下文默认没有装配MultipartResolver,故要使用上传文件操作,还需要在配置文件中注册MultipartResolver。

<!-- 文件上传配置 -->
<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>

此外,还需要Apache Commons FileUpload的组件,将commons-fileupload-1.3.3.jar、commons-io-2.6.jar复制到项目lib下。

Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器

  举个例子:

Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器

引入的jar包为spring-5.0.5、commons-logging-1.2.jar、commons-io-2.6.jar、commons-fileupload-1.3.3.jar。error.jsp、success.jsp仅是提示页面。

springmvc-config.xml

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:default-servlet-handler/> <!-- 扫描controller -->
<context:component-scan base-package="com.lfy.controller"/> <!-- 映射器、适配器策略 -->
<mvc:annotation-driven/> <!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/content/" p:suffix=".jsp" /> <!-- 文件上传配置 -->
<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>
</beans>

uploadForm.jsp

<%@ 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>文件上传</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>

FileUploadController.java

package com.lfy.controller;

import java.io.File;
import java.net.URLEncoder; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.FileUtils;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile; import com.lfy.bean.User; @Controller
public class FileUploadController{ @RequestMapping(value="/uploadForm")
public String uploadForm(){
// 跳转到文件上传页面
return "uploadForm";
} // 上传文件会自动绑定到MultipartFile中
@RequestMapping(value="/upload")
public String upload(HttpServletRequest request,
@RequestParam("description") String description,
@RequestParam("file") MultipartFile file) throws Exception{ System.out.println(description);
// 如果文件不为空,写入上传路径
if(!file.isEmpty()){
// 上传文件路径
String path = request.getServletContext().getRealPath(
"/images");
// 上传文件名
String filename = file.getOriginalFilename();
File filepath = new File(path,filename);
// 判断路径是否存在,如果不存在就创建一个
if (!filepath.getParentFile().exists()) {
filepath.getParentFile().mkdirs();
}
// 将上传文件保存到一个目标文件当中
file.transferTo(new File(path+File.separator+ filename));
System.out.println("上传文件路径:" + (path+File.separator+ filename));
return "success";
}else{
return "error";
}
}
}

总结:步骤1.引入commons相关jar包;2.注册MultipartResolver;3.编码接收文件的上传。

==>使用对象接收文件的上传:文件作为对象的属性保存

registerForm.jsp

<%@ 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>用户注册</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>

User.java

package com.lfy.bean;

import java.io.Serializable;

import org.springframework.web.multipart.MultipartFile;

public class User implements Serializable{

    private static final long serialVersionUID = 1L;

    private String username;

    private MultipartFile image;

    public User() {
super();
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public MultipartFile getImage() {
return image;
}
public void setImage(MultipartFile image) {
this.image = image;
}
}

UserImageUpController.java

package com.lfy.controller;

import java.io.File;
import java.net.URLEncoder; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.FileUtils;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import com.lfy.bean.User; @Controller
public class UserImageUpController{ @RequestMapping(value="/registerForm")
public String registerForm(){
// 跳转到文件上传页面
return "registerForm";
} @RequestMapping(value="/register")
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(new File(path+File.separator+ filename));
// 将用户添加到model
model.addAttribute("filename", user.getImage().getOriginalFilename());
System.out.println("上传文件路径:" + (path+File.separator+ filename));
return "userInfo";
}else{
return "error";
}
} @RequestMapping(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));
}
}

上面完成一个头像的上传功能,并将文件信息存储在User中,然后将这些信息传递到userInfo.jsp,点击对应的连接,再由download()方法处理下载。

Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器

2、文件下载。

download处理方法接收到页面传递的文件名filename后,使用Apache Commons FileUpload组件的FileUtils读取项目的images文件夹下的该文件,并将其构建成ResponseEntity对象返回客户端下载。使用它可以很方便的定义返回的HttpHeaders和HttpStatus。

代码中MediaType,代表的是Internet Media Type,即互联网媒体类型,也叫做MIME类型。在Http协议消息中,使用Content-Type来表示具体请求中的媒体类型信息。HttpStatus类型代表的是Http协议中的状态。

3、拦截器

Interceptor拦截器,主要作用是拦截用户的请求并进行相应的处理。如用于用户权限验证判断用户是否已经登录

Spring MVC中的Interceptor拦截器拦截请求是通过实现HandlerInterceptor接口完成的。spring MVC中定义一个Interceptor拦截器非常简单,只需要实现HandlerInterceptor接口,或者继承抽象类HandlerInterceptorAdapter。

HandlerInterceptor接口定义了三个方法,spring MVC通过这三个方法来对用户的请求进行拦截处理:

Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器

Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器

举个例子:强制登录拦截

Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器

引入的jar包为spring、commons-logging、javax.servlet.jsp.jstl-1.2.1.jar、javax.servlet.jsp.jstl-api-1.2.1.jar。

web.xml引入springmvc-config.xml文件。

springmvc-config.xml

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:default-servlet-handler/> <context:component-scan base-package="com.lfy.controller"/> <!-- 映射器、适配器策略 -->
<mvc:annotation-driven/> <!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/content/" p:suffix=".jsp" /> <!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors下面的Interceptor将拦截所有的请求 -->
<bean class="com.lfy.Interceptor.AuthorizationInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>

loginForm.jsp

<%@ 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>登录页面</title>
</head>
<body>
<h3>登录页面</h3>
<form action="login" method="post">
<!-- 提示信息 -->
<font color="red">${requestScope.message }</font>
<table>
<tr>
<td><label>登录名: </label></td>
<td><input type="text" id="loginname" name="loginname" ></td>
</tr>
<tr>
<td><label>密码: </label></td>
<td><input type="password" id="password" name="password" ></td>
</tr>
<tr>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>

mian.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!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>首页</title>
<style type="text/css">
table{border-collapse:collapse;border-spacing:0;border-left:1px solid #888;border-top:1px solid #888;background:#efefef;}
th,td{border-right:1px solid #888;border-bottom:1px solid #888;padding:5px 15px;}
th{font-weight:bold;background:#ccc;}
</style>
</head>
<body>
<h3>欢迎[${sessionScope.user.username }]访问</h3>
<br>
<table border="1">
<tr>
<th>封面</th><th>书名</th><th>作者</th><th>价格</th>
</tr>
<c:forEach items="${requestScope.book_list }" var="book">
<tr>
<td><img src="data:images/${book.image }" height="60"></td>
<td>${book.name }</td>
<td>${book.author }</td>
<td>${book.price }</td>
</tr>
</c:forEach>
</table>
</body>
</html>

AuthorizationInterceptor.java

package com.lfy.Interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.lfy.bean.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; /**
* 拦截器必须实现HandlerInterceptor接口
* */
public class AuthorizationInterceptor implements HandlerInterceptor { // 不拦截"/loginForm"和"/login"请求
private static final String[] IGNORE_URI = {"/loginForm", "/login"}; /**
* 该方法将在整个请求完成之后执行, 主要作用是用于清理资源的,
* 该方法也只能在当前Interceptor的preHandle方法的返回值为true时才会执行。
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception exception)
throws Exception {
System.out.println("AuthorizationInterceptor afterCompletion --> "); }
/**
* 该方法将在Controller的方法调用之后执行, 方法中可以对ModelAndView进行操作 ,
* 该方法也只能在当前Interceptor的preHandle方法的返回值为true时才会执行。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView mv) throws Exception {
System.out.println("AuthorizationInterceptor postHandle --> "); } /**
* preHandle方法是进行处理器拦截用的,该方法将在Controller处理之前进行调用,
* 该方法的返回值为true拦截器才会继续往下执行,该方法的返回值为false的时候整个请求就结束了。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("AuthorizationInterceptor preHandle --> ");
// flag变量用于判断用户是否登录,默认为false
boolean flag = false;
//获取请求的路径进行判断
String servletPath = request.getServletPath();
// 判断请求是否需要拦截
for (String s : IGNORE_URI) {
if (servletPath.contains(s)) {
flag = true;
break;
}
}
// 拦截请求
if (!flag){
// 1.获取session中的用户
User user = (User) request.getSession().getAttribute("user");
// 2.判断用户是否已经登录
if(user == null){
// 如果用户没有登录,则设置提示信息,跳转到登录页面
System.out.println("AuthorizationInterceptor拦截请求:");
request.setAttribute("message", "请先登录再访问网站");
request.getRequestDispatcher("loginForm").forward(request, response);
}else{
// 如果用户已经登录,则验证通过,放行
System.out.println("AuthorizationInterceptor放行请求:");
flag = true;
}
}
return flag;
}
}

User.java

package com.lfy.bean;

import java.io.Serializable;

public class User implements Serializable{

    private static final long serialVersionUID = 1L;

    private Integer id;            // id
private String loginname; // 登录名
private String password; // 密码
private String username; // 用户名 public User() {
super();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLoginname() {
return loginname;
}
public void setLoginname(String loginname) {
this.loginname = loginname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
} @Override
public String toString() {
return "User [id=" + id + ", loginname=" + loginname + ", password="
+ password + ", username=" + username + "]";
}
}

Book.java

package com.lfy.bean;

import java.io.Serializable;

public class Book implements Serializable{

    private static final long serialVersionUID = 1L;

    private Integer id;                // id
private String name; // 书名
private String author; // 作者
private Double price; // 价格
private String image; // 封面图片 public Book() {
super();
}
public Book( String image,String name, String author, Double price) {
super();
this.image = image;
this.name = name;
this.author = author;
this.price = price;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
} public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
@Override
public String toString() {
return "Book [id=" + id + ", name=" + name + ", author=" + author
+ ", price=" + price + ", image=" + image + "]";
}
}

FormController.java

package com.lfy.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; /**
* 动态页面跳转控制器
* */
@Controller
public class FormController{ @GetMapping(value="/loginForm")
public String loginForm(){
// 跳转到登录页面
return "loginForm";
}
}

UserController.java

package com.lfy.controller;

import javax.servlet.http.HttpSession;

import com.lfy.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView; /**
* 处理用户请求控制器
* */
@Controller
public class UserController { /**
* 处理/login请求
* */
@PostMapping(value="/login")
public ModelAndView login(
String loginname,String password,
ModelAndView mv,
HttpSession session){
// 模拟数据库根据登录名和密码查找用户,判断用户登录
if(loginname != null && loginname.equals("lfy")
&& password!= null && password.equals("123456")){
// 模拟创建用户
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
user.setUsername("管理员");
// 登录成功,将user对象设置到HttpSession作用范围域
session.setAttribute("user", user);
// 转发到main请求
mv.setViewName("redirect:main");
}else{
// 登录失败,设置失败提示信息,并跳转到登录页面
mv.addObject("message", "登录名或密码错误,请重新输入!");
mv.setViewName("loginForm");
}
return mv;
}
}

BookController.java

package com.lfy.controller;

import java.util.ArrayList;
import java.util.List;
import com.lfy.bean.Book;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; /**
* 处理图书请求控制器
* */
@Controller
public class BookController {
/**
* 处理/main请求
* */
@RequestMapping(value="/main")
public String main(Model model){
// 模拟数据库获得所有图书集合
List<Book> book_list = new ArrayList<Book>();
book_list.add(new Book("java.jpg","疯狂Java讲义(附光盘)","李刚 编著",74.2));
book_list.add(new Book("ee.jpg","轻量级Java EE企业应用实战","李刚 编著",59.2));
book_list.add(new Book("sql0.png","mysql技术内幕-sql编程","姜成尧 编著",60.6));
book_list.add(new Book("sql1.png","mysql调优","贺春旸 编著",66.6));
// 将图书集合添加到model当中
model.addAttribute("book_list", book_list);
// 跳转到main页面
return "main";
}
}

运行结果:

Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器