JavaWeb之filter&listener&文件上传
一、filter
1.1 过滤器的基本概述:
filter是Servlet规范中的一个接口(javax.servlet.Filter接口),要想实现过滤器功能,必须实现此接口。
JavaWeb中的过滤器:当用户访问服务器资源时,过滤器将请求拦截下来,完成一些特殊的功能(如登录验证、统一编码处理、敏感字符过滤),还可以对响应信息进行拦截过来(一般是用来做数据压缩的)。
接口中有三个方法:
init
:用于初始化doFilter
:每次执行拦截过滤destroy
:用于销毁
1.2 过滤器快速使用
1.2.1 编码步骤
- 客户端向服务器发起访问资源的请求
- Filter将请求拦截住,开始处理访问资源之前的逻辑
- Filter决定是否要放行访问请求,如果放行,请求继续向后运行
- 请求访问到相关资源,然后服务器给出响应
- Filter将响应拦截住,开始处理访问资源之后的逻辑
- 服务器将响应返回给浏览器
1.2.2 Filter配置
xml:
<filter>
<filter-name>别名</filter-name>
<filter-class>配置过滤器的全路径
</filter>
<filter-mapping>映射配置
<filter-name>别名</filter-name>
<url-pattern>filter的拦截路径</url-pattern>
</filter-mapping>
注解:
@WebFilter(value="指定拦截规则") //value属性和urlPatterns属性的作用是一样的
1.2.3 Filter执行过程
单个过滤器:
- 请求访问资源之前,先到达过滤器
- 过滤器放行之后,会访问资源
- 访问资源之后,还会再次经过过滤器
多个过滤器
- 请求访问资源之前,先到达过滤器
- 过滤器放行之后,执行下一个过滤器,直到所有过滤器执行完之后,才会访问资源
- 只要其中任何一个过滤器没有放行,就不会访问资源
- 访问资源之后,还会按照倒序方式执行过滤器
过滤器的放行:
- 调用
FilterChain
接口中的doFilter
方法,如果有下一个过滤器,则执行下一个过滤器;如果当前过滤器处于调用的最后一个过滤器,就执行目标资源。
多个过滤器的执行顺序
XML方式:
- 看
<filter-mapping>
的配置先后顺序
注解方式:
- 看过滤器的类名的字符,按照ASCII码表的顺序,一个字母一个字母的比,字母排在前面的先执行
1.3 Filter的生命周期
创建:应用加载时,Filter对象创建
void init(FilterConfig config)
:初始化方法,在项目启动的时候就会执行,只执行一次
活着:只要应用提供服务,Filter对象就一直存在
void doFilter(request,response,FilterChain chain)
:执行过滤方法,当请求匹配上的时候就会执行
销毁:应用卸载或者服务器宕机,对象销毁
void destroy()
:销毁,在项目被移除的时候或者服务器关闭的时候就会执行,只执行一次
1.4 拦截方式配置
默认情况下,Fliter只拦截请求,请求到达服务器的时就开始拦截。
可以通过<filter-mapping>
标签中dispatcher
标签来配置过滤拦截方式:
<dispatcher>
REQUEST</dispatcher>
默认值,只拦截从浏览器直接发送过来的请求<dispatcher>
FORWARD</dispatcher>
过滤拦截转发过来的请求<dispatcher>
INCLUDE</dispatcher>
过滤拦截包含,只有动态包含(与静态包含无关,因为静态包含是一个文件)<dispatcher>
ERROR</dispatcher>
过滤拦截全局错误页面,一般不用<dispatcher>
ASYNC</dispatcher>
过滤拦截异步请求,一般不用
1.5 Filter与servlet的区别
- Servlet的功能过滤器全都有,而过滤器多了一个放行的功能
- 过滤器它不是一个资源,不能通过地址栏输入访问
- Servlet是一个资源,可以通过地址栏访问
二、监听器——Listener
2.1 Listener概述
在web程序中,Listener监视某些对象(域对象),一旦其发生相应的变化,就采取相应的操作,主要应用于 统计历史访问次数、统计在线人数、系统启动时初始化配置信息。
Listener主要概念:
- 事件源:发生事件的对象
- 事件:发生了什么事情
- 监听器:是一个接口,这里面提供了一些方法需要我们自己编写
Listener是web核心中三大组件之一,
- Servlet:服务器的小程序 , 程序的入口
- Filter:过滤器
- Listener:监听器
八个监听器接口:
监听对象创建和销毁的
ServletContextListener
ServletRequestListener
HttpSessionListener
监听域中属性发生变化的(变化有:存入域中,修改域中属性,从域中删除)
ServletContextAttributeListener
ServletRequestAttributeListener
HttpSessionAttributeListener
感知型监听器(都和会话域有关系)
HttpSessionBindingListener
感知有数据和会话域绑定
感知有数据和会话域解绑HttpSessionActivtionListener
感知会话域中数据被钝化到磁盘上
感知磁盘上的数据被活化到会话域中
三、文件上传
3.1 文件上传概述
文件上传:要将客户端(浏览器)文件传递到服务器端存储,不是直接存储到数据库中,而是要将文件存储到服务器所在的磁盘上,这就要使用文件上传。
使用文件上传方式的好处:通过文件上传,可以将浏览器端的大数据直接保存到服务器端。不将数据保存到数据库中,而是保存到服务器磁盘上,这样减少了数据库服务器的压力,对数据的操作更加灵活。
3.2 常见的文件上传技术
- 使用apache提供的工具:
commons-fileupload.jar
(代码写起来比较复杂) - 使用servlet3.0开始提供的文件上传方式
- 使用springmvc框架(非常简单,底层封装了commons-fileupload)
3.3 文件上传浏览器前提
- 表单的提交方式必须是post
- 表单的enctype属性值必须是multipart/form-data(多部分表单数据)
- 在表单中提交文件选择框
<input type='file'>
,且需要提供name属性
3.4 使用commons-fileupload方式上传
- 导入jar包
- 创建磁盘文件项工厂,用来设置临时文件的大小和目录.
- 创建核心上传对象
- 使用核心上传对象解析请求,返回一个list<文件项>
- 遍历list,获取每个文件项
- 判断是否为普通表单项(文件上传项之外)
- 若是:获取name属性的值及用户输入的内容
- 若不是:
- 获取name属性的值
- 获取文件的名称
- 获取文件的内容
- 保存文件
- 删除临时文件
- 判断是否为普通表单项(文件上传项之外)
@WebServlet(name = "UploadServlet" , urlPatterns = "/uploadServlet")
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//借助第三方帮我们解析这个流(apache 提供fileUpload.jar包)
//步骤如下:
//步骤1: 创建磁盘文件项工厂,操作本地磁盘使用(例如创建临时文件...)
DiskFileItemFactory factory = new DiskFileItemFactory();
//步骤2: 创建解析流的对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//步骤3: 开始解析-> 获得集合对象 List<FileItem>
List<FileItem> list = fileUpload.parseRequest(request);
//步骤4: 循环遍历
for (FileItem fileItem : list) {
//步骤5: 获得普通表单数据 和 文件上传数据
//判断是普通的数据 还是文件上传数据 isFormField()是否为普通表单字段
boolean formField = fileItem.isFormField();
if(formField){
//普通表单数据 username=jack nickname = rose
String name = fileItem.getName(); // null
String fieldName = fileItem.getFieldName();//表单提交key
String string = fileItem.getString("utf-8");// 表单提交的value
//
System.out.println(name);
System.out.println(fieldName);
System.out.println(string);
}else{
//文件上传数据
String name = fileItem.getName(); // 文件的名称
String fieldName = fileItem.getFieldName();//表单提交key
//String string = fileItem.getString("utf-8");// 表单提交的value 文件的内容
//输入流
InputStream is = fileItem.getInputStream();
//截取后缀名
name = name.substring(name.lastIndexOf("."));
//保证文件名称不重复
String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String filePath = "D:/"+date+name;//name是有后缀名 .jpg
//输出流
FileOutputStream os = new FileOutputStream(filePath);
//快速IO流操作
IOUtils.copy(is, os);
is.close();
os.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.5 Servlet3.0文件上传
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
文件上传有表单三要素:
1.method必须是post提交(上传没有限制)
2.表单中必须有一个组件 type="file"
3.表单必须是多部分表单上传( 在表单上加入这个属性: enctype="multipart/form-data" )
注意加入属性后 request对象获得数据的api 失效
--%>
<form action="/upload2Servlet" method="post" enctype="multipart/form-data">
<input type="text" name="username"><br/>
<input type="text" name="nickname"><br/>
<input type="file" name="pic"><br/>
<input type="submit">
</form>
</body>
</html>
准备Servlet,加一个注解 @MultipartConfig 文件上传解析数据
@WebServlet(name = "Upload2Servlet" , urlPatterns = "/upload2Servlet")
@MultipartConfig //servlet 3.0提供的注解 目的 解析request对象 ,恢复原来的这些api
public class Upload2Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String nickname = request.getParameter("nickname");
System.out.println(username);
System.out.println(nickname);
//处理文件 servlet 3.0提供api request.getPart("key") 等效 request.getParameter("key");
Part pic = request.getPart("pic");
//判断文件是否存在
if(pic!=null && pic.getSize() > 0){
//解析文件
String fileName = pic.getSubmittedFileName();//文件名称
fileName = fileName.substring(fileName.lastIndexOf("."));
String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String filePath = "D:/" + date + fileName ;
//输入流
InputStream is = pic.getInputStream();
//输出流
FileOutputStream os = new FileOutputStream(filePath);
//快速IO流操作
IOUtils.copy(is, os);
is.close();
os.close();
}
}
}