【web第十八天】Listener监听器+文件上传
Day17-Listener监听器
1.监听器概述
Servlet技术中三大组件之一 - Servlet Filter Listener
Servlet技术规范中sun预先定义了八大监听器用来监听web开发中对应的事件。
监听器可以用来监听web应用执行过程中相关的事件,来进行对应的处理。
2.开发监听器步骤
(1)写一个监听器类: 要求必须实现对应的监听器接口,如:
public class MyXxxListener implements XxxListener{...}
(2)注册监听: 在web.xml中配置完成注册
<listener>
<listener-class>cn.tedu.listener.MyXxxListener</listener-class>
</listener>
3.javaweb中的监听器: (八个)
3.1.监听三大作用域创建和销毁的监听器(三个)
ServletContextListener
生命周期:在web应用启动时创建出来,之后一直驻留在内存中唯一的代表当前web应用,直到web应用移除出容易或服务器关闭时,随着web应用的销毁ServletContext对象跟着被销毁。
作用范围:整个web应用范围
主要功能:在整个web应用范围内,整个web应用存活期间,共享数据
HttpSessionListener
生命周期:在第一次调用request.getSession()时创建,自杀: session.invalidate,超时:30分钟,意外身亡:服务器非正常关闭
作用范围:整个会话
主要功能:在整个会话范围内,整个会话的生命周期期间,共享数据
钝化:在服务器正常关闭时,仍然在存活期间的session,会被序列化后 保存在tomcat的work目录下,这个过程称之为session的钝化
活化:在服务器正常启动时,会将钝化的session再恢复到内存中,继续 使用,这个过程称之为session的活化
ServletRequestListener,
生命周期:请求开始时创建,请求结束时销毁
作用范围:整个请求
主要功能:在请求过程中,在请求范围内,共享数据
这三个接口中都定义了两个方法来处理被监听对象中的创建和销毁事件。
void contextInitialized(ServletContextEvent sce) 创建ServletContext时调用
void contextDestroyed(ServletContextEvent sce) 销毁ServletContext是调用
3.2.监听三大作用域中属性变化的监听器(三个)
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件。
void attributeAdded(ServletContextAttributeEvent scae) 添加属性时调用
void attributeReplaced(ServletContextAttributeEvent scae) 替换属性时调用
void attributeRemoved(ServletContextAttributeEvent scae) 删除属性时调用
3.3.监听JavaBean在Session域中状态变化的监听器
HttpSessionBindingListener:
使JavaBean 可以感知自己被绑定到 Session 中和从 Session 中删除的监听器。
//当对象被绑定到 HttpSession 对象中时触发
void valueBound(HttpSessionBindingEvent event)
//当对象从 HttpSession 对象中解除绑定时触发
void valueUnbound(HttpSessionBindingEvent event)
HttpSessionActivationListener:
使javabean可以感知自己在session域中随着session被钝化、活化的状态变化的监听器
//当 HttpSession 对象中的对象将要被钝化时触发
sessionWillPassivate(HttpSessionBindingEvent event)
//当 HttpSession 对象中的对象将要被活化时触发(注意:javabean对象如果需要被活化,需要实现序列化接口:Serializable)
void sessionDidActive(HttpSessionBindingEvent event)方法。
这两个监听器比较特殊,不需要单独写类来实现,也不需要在web.xml中进行配置,只需要让javabean自己来实现即可。
1.EasyMall中监听器的应用
代码如下:
在src下创建包:cn.tedu.listener,创建类:MyServletContextListener,实现ServletContextListener接口,并在contextInitialized方法中添加如下代码: public void contextInitialized(ServletContextEvent sce) { ServletContext sc = sce.getServletContext(); sc.setAttribute("app", sc.getContextPath()); }
|
修改所有jsp文件中的获取web应用虚拟路径的el表达式,将: ${ pageContext.request.contextPath }改为:${ app } |
文件上传
1.文件上传概述
1.1.文件上传是什么
在web开发中经常需要从客户端向服务器上传文件,如:上传照片、上传新闻图片、上传附件等等。这些都需要通过WEB开发中的文件上传技术实现。
1.2.文件上传步骤
实现web开发中的文件上传功能只需要两个步骤:
(1)提供一个带有文件上传项的表单。
(2)在servlet中读取处理上传的文件,保存到服务器中。
2.文件上传实现
2.1.在用户页面中添加上传输入项
提供一个带有文件上传项的表单
<input type="file" name="filex"/>
注意:
(1)文件上传的输入框必须有name属性才能被上传
(2)文件上传的表单必须是post提交
(3)文件上传的表单必须设置enctype=multipart/form-data
2.2.在服务器端编写文件上传程序
方式一:
可以通过javaee原生的api中request.getInputStream来获取请求中实体内容的流,获取到流中的数据后,解析、处理非常麻烦
方式二:
为了简化这个处理过程,Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件(Commons-fileupload ),让开发人员轻松实现web文件上传功能。
2.3.上传组件(Apache commons-fileupload)使用过程
首先需要下载并导入该组件相应的支撑jar包: Commons-fileupload和commons-io(课前资料中已经提供)
然后编程实现,步骤如下:
(1)创建文件上传工厂类:DiskFileItemFactory对象,设置缓冲区大小和临时文件目录。
DiskFileItemFactory factory = new DiskFileItemFactory(sizeThreshold, repository);
创建时,需要指定内存缓冲区大小和临时文件存放位置。
sizeThreshold - 指定内存缓冲区的大小
repository - 指定临时文件存放的位置
文件上传时,需要将请求的实体内容全部读取后,才能做处理,此时需要将实体内容缓冲起来。内存缓冲快,但是耗费内存;文件缓冲慢,但是可以存放大量数据。
所以此处提供了两个选项:如果数据大小小于sizeThreshold,则使用内存做缓冲,速度快。如果文件大小超过了该限制,则在repository指定的位置下创建临时文件来缓冲数据。
(2)创建文件上传核心类:使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。
ServletFileUpload fileUpload = new ServletFileUpload(factory);
常用方法有:
//判断当前表单是否是一个enctype为multipart/form-data类型的表单
boolean isMultipartContent(HttpServletRequest request)
//设置单个上传文件的最大值。
setFileSizeMax(long fileSizeMax)
//设置上传文件总量的最大值。
setSizeMax(long sizeMax)
//解决上传文件名乱码问题。
setHeaderEncoding(String encoding)
//实时监听文件上传状态。
setProgressListener(ProgressListener pListener)
//解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。
List parseRequest(HttpServletRequest request)
- 2. 对list进行迭代,得到文件上传项 FileItem,并判断是否是上传文件。
//判断当前FileItem是不是一个普通字段项,如果返回true表示这是一个普通字段项,返回false表示是一个文件上传项
boolean isFormField()
//如果是普通字段项
String getFieldName() //获取字段项的名称
String getString(String encode) //获取字段项的值,可以用encode属性进行设置。
//如果是文件上传项
String getName() //获取文件名(有些浏览器会携带客户端路径)
InputStream getInputStream() //获取文件内容的流
delete() //在关闭FileItem输入流后,删除临时文件
2.4.上传文件注意问题
(1)ie上传文件时,文件名Bug处理
当ie上传文件时,文件名称会携带路径。
if(fname.contains("\\")){
fname = fname.substring(fname.lastIndexOf("\\"));
}
(2)文件上传保存位置问题
文件上传保存的位置一定不能被外界直接访问,防止用户用浏览器访 问、下载资源或执行jsp恶意代码。
所以,文件要么保存在WEB-INF下保护起来,要么放在本地磁盘其他位 置,保证通过浏览器无法直接访问。
(3)上传文件重名问题
多个上传名称相同时,文件会发生覆盖。应该想办法让文件名尽量不要重复。
在文件名的前面拼接UUID来保证文件名绝对不会重复:
fname = UUID.randomUUID().toString()+"_"+fname;
(4)上传文件目录下文件过多
一个文件夹下文件过多,会造成访问缓慢,甚至有可能无法访问。所以,应该想办法将这些文件分目录存储。
分目录存储的算法可以有很多:
1.按照上传时间进行目录分离 (周、月 )
2.按照上传用户进行目录分离 ----- 为每个用户建立单独目录
3.按照固定数量进行目录分离 ------ 假设每个目录只能存放3000个文件 ,每当一个目录存满3000个文件后,创建一个新的目录
4.按照唯一文件名的hashcode 进行目录分离
计算文件名的hash - 散列
将hash值转换为16进制的字符串 - 共8位 值0-9 a-f
将这个hash字符串截取出每一位作为一级目录
最终将上文件放置到8级hash目录下,共有16^8个可能的文件夹分目录存放文件,从而保证一个文件夹文件不会过多
2.5.文件上传进度监听
ServletFileUpload类提供了如下方法,监听文件上传时的进度信息:
public void setProgressListener(ProgressListener pListener)
设置监听器,文件上传程序会自动执行,监听器中 update方法
public void update(long pBytesRead, long pContentLength, int pItems)
在方法中可以获得 文件总大小、已经上传大小和 上传第几个元素
能否根据上面三个参数计算:剩余大小、传输速度、已用时间、剩余时间
(1) 已用时间 = 当前时间 - 开始时间
(2) 速度 = 已经上传大小/已用时间
(3) 剩余大小 = 总大小- 已经上传大小
(4) 剩余时间 = 剩余大小/速度