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上下文默认没有装配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下。
举个例子:
引入的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()方法处理下载。
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通过这三个方法来对用户的请求进行拦截处理:
举个例子:强制登录拦截
引入的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="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"; } }
运行结果: