JavaWeb中的高级知识总结
- 知识结构图
- 文件下载
默认情况下,如果浏览器可以处理Content-Type响应头中指定数据类型,浏览器就会直接处理,比如显示出HTML页面(text/html),或者显示出照片(image/png)等;如果浏览器不知道怎么处理,就会以文件的形式下载到本地电脑上
要想强制让浏览器以文件的方式下载,可以设置Content-Disposition=attachment;filename=xxx
@WebServlet("/download") public class DownloadServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String filename = "人丑就要多读书.jpg"; //如果文件名由非西文字符,最好使用URL编码一遍 //文件下载时,各个浏览器对URL编码过的文件名处理不一样,也没法统一处理 filename = URLEncoder.encode(filename, "UTF-8"); //响应类型最后也设置 response.setContentType("image/jpeg"); response.setHeader("Content-Disposition", "attachment;filename=" + filename); InputStream input = getServletContext().getResourceAsStream("/人丑就要多读书.jpg"); OutputStream output = response.getOutputStream(); //把文件数据作为响应体内容输出到浏览器 byte[] buff = new byte[1024 * 8]; int len = 0; while ((len = input.read(buff)) > -1) { output.write(buff, 0, len); } //关闭自己创建的input,output不用手动关闭,服务器会自动关闭 input.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- 文件上传
客户端表单中必须指定method=post,因为上传的文件可能很大,并且指定enctype=multipart/form-data使用上传文件专门的编码方式
另外客户端还需要使用<input type="file"> 选择要上传的文件
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>文件上传</title> </head> <body> <span style="color: red">${ requestScope.message }</span> <form action="/webDemo/upload" method="post" enctype="multipart/form-data"> <input type="file" name="uploadFile" /> <br/><br/> <textarea rows="5" cols="50" name="comment" placeholder="文件说明"></textarea> <br/><br/> <input type="submit" value="上传" /> </form> </body> </html>
服务器端Servlet需要标注@MultipartConfig,否则会忽略文件内容
普通请求参数处理方式不变,先request.setCharacterEncoding("UTF-8")设置post请求参数编码,然后request.getParameter()获取请求参数
上传的文件内容会先被保存到服务器端的临时文件中,可以通过request.getPart(name)获得封装了上传文件信息的Part对象,从Part对象中可以获得临时文件读取流、原始文件名称、文件大小等有用信息
直接把临时文件copy到想要上传的目录下即可,注意原始文件名可能会重复,应该为文件生成新的唯一文件名,然后在数据库中同时保存原始文件名和新文件名,这样原始文件名信息就不会丢失
注意如果把文件上传到服务器部署目录的项目目录下面,项目修改重新部署时就会把以前上传的文件清理掉,解决办法是使用外面的目录作为上传目录,或者把外面的目录配置成服务器的虚拟目录
@WebServlet("/upload") @MultipartConfig public class UploadServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String comment = request.getParameter("comment");//文件说明 //这个时候文件数据已经上传并保存到了服务器临时文件中 //Part封装了上传文件的信息 Part part = request.getPart("uploadFile"); String origFilename = part.getSubmittedFileName();//原始文件名 String inputName = part.getName();//文件表单控件name值 InputStream input = part.getInputStream();//上传到服务器的临时文件读取流 //接下来只需要把临时文件copy到想要保存的目录下 String targetDir = getServletContext().getRealPath("/upload"); //为避免文件名重复,不能使用原始文件名,可以使用UUID生成新的文件名 String realFileame = UUID.randomUUID().toString(); OutputStream output = new FileOutputStream(new File(targetDir, realFileame)); byte[] buff = new byte[1024 * 8]; int len = 0; while ((len = input.read(buff)) > -1) { output.write(buff, 0, len); } input.close(); output.close(); // JDBCUtils.executeUpdate("insert into T_Files( origFilename, realFileame, comment)", origFilename, realFileame, comment); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- Filter
Filter 过滤器,使用“拦截器”更容易理解,Filter可以在请求到达Servlet之前拦截请求,以便做一些预处理和后处理
Filter和Servlet在使用方式上非常相似,甚至可以相互代替,但我们通常不这么做。他们在功能上各有分工:Servlet专注于处理业务逻辑,Filter专注于其他非业务逻辑,比如设置post请求参数编码、检查权限等
使用Filter的步骤:
1 实现自己的Filter类,并实现doFilter方法
2 使用@WebFilter标注Filter,并指定Filter的虚拟路径,因为Filter专门执行预处理,所以Filter和Servlet的虚拟路径需要重叠
注意 在Filter中 /* 才表示匹配全部请求,这点和Servlet( / )有差别
@WebFilter("/*") public class CharsetFilter implements Filter { @Override public void init(FilterConfig fConfig) throws ServletException { }
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //这样Servlet就不用单独设置post请求参数编码了 request.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); }
@Override public void destroy() { } }
Filter生命周期:和Servlet不同,Filter在项目启动时就创建好,而且以单例的形式存在,项目停止时销毁
Filter工作方式:服务器收到一个请求,先查找有没有匹配的Filter,如果有,则先把请求交给Filter处理,即调用FIlter的doFilter()方法。
如果有多个Filter匹配,匹配的Filter就会和Servlet一起组成调用链。当前Filter只有在调用了chain.doFilter()方法后,请求才会传递到调用链的下一个节点
如果Filter在web.xml中配置,多个Filter的调用顺序就是配置顺序,如果使用@WebFilter配置,调用顺序就是Filter类名称自然排序的顺序
- Listener
在Web项目整个生命周期过程中,会发生很多事件,比如ServletContext的创建、销毁,HttpSession的创建、销毁等,在发生这些事件时,如果我们希望做一些事情,就可以通过Listener监听器给这些事件注册事件处理程序
事件种类有很多,每种事件都有对应Listener注册处理程序。但整体上Listener使用不多,我们只学习ServletContextListener和HttpSessionActivationListener
ServletContextListener
该接口提供了contextInitialized()和contextDestroyed()两个方法分别注册项目启动、项目停止时的事件处理程序
当项目启动即ServletContext对象创建成功后,服务器就会回调contextInitialized()方法
当项目停止即ServletContext销毁前,服务器就会回调contextDestroyed()方法
@WebListener public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContext对象创建完成,项目启动成功"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("项目即将停止"); } }
session钝化和活化
很多web服务器都支持session钝化和活化
序列化是指把内存中的对象按照一定的规则转化为"一串"二进制数据,方便保存到硬盘或者通过网络发送到另外一台电脑。
一个类只有实现了Serializable接口才可以被序列化
为了判断一个类是否被修改过,通常使用long serialVersionUID字段作为类的"版本"
钝化是指把内存中的session对象序列化到硬盘上(session的实现类StandardSession实现了Serializable接口)
活化是指把保存到硬盘上的session数据反序列化为内存中的session对象
用途:
可以把达到一定空闲时间的session钝化到硬盘上,减少内存占用
当web服务器关闭或者重启时,可以把内存中的session全部钝化到硬盘上
注意:钝化某个session后,只有当用户再次访问时,才会活化该session
session默认是不会被钝化的,需要在项目中配置开启:
<?xml version="1.0" encoding="UTF-8"?> <Context> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="10"> <Store className="org.apache.catalina.session.FileStore" directory="sessionPassivationDir"/> </Manager> </Context>
maxIdleSwap:session空闲时间超过此时间就可以被钝化,单位秒,默认-1表示永不钝化
directory:钝化文件存储在哪个目录下(从Tomcat的work目录往下找)
注意:Tomcat大概每隔60秒检查一次哪些session需要被钝化,所以session达到钝化条件后可能并不会立刻出现钝化效果。此外,钝化文件会在session过期后被删除
HttpSessionActivationListener
放入session中的属性只有实现了Serializable接口才会随session一起钝化、活化
如果需要监听属性随session一起钝化、活化的事件,属性类需要实现HttpSessionActivationListener接口
public class User implements Serializable, HttpSessionActivationListener { private Long id; private String email; private String name; private String password; private Date birthday; @Override //钝化前服务器会回调此方法 public void sessionWillPassivate(HttpSessionEvent se) { System.out.println("本user对象将要随着session一起钝化"); } @Override //活化后服务器会回调此方法 public void sessionDidActivate(HttpSessionEvent se) { System.out.println("本user对象已经随着session一起活化了"); }