本文主要对Filter的使用进行讲解。
Filter 技术是servlet 2.3 新增加的功能。Filter翻译过来的意思就是过滤器,能对客户端的消息进行预处理,然后将请求转发给其它的web组件,可以对ServletHttpRequest和ServletHttpResponse进行修改和检查。例如:在Filter中可以检查用户是否登录,对未登录的用户跳转到登陆界面。
要定义一个过滤器,则需要实现javax.servlet.Filter接口,一个过滤器中包含了三个与生命周期相关的方法:
第一步:创建DemoFilter.java
package cn.zq.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class DemoServlet implements Filter{ public void init(FilterConfig config) throws ServletException { System.out.println("DemoServlet.init..."); } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { System.out.println("DemoServlet.doFilter..."); System.out.println("this = " + this); } public void destroy() { System.out.println("DemoServlet.destroy..."); } }
<filter> <filter-name>demo</filter-name> <filter-class>cn.zq.filter.DemoServlet</filter-class> </filter> <filter-mapping> <filter-name>demo</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
启动tomcat,可以看到如下输出:
说明Filter的init方法在web容器启动的时候执行,读者可以自行验证destroy()方法会在web容器停止时执行,访问主页:
在访问主页的时候浏览器是一片空白的,控制台输出上面的消息,Filter只会实例化一次,为什么我们访问不到我们要访问的内容呢?只需要在doFilter方法中加入如下的语句就可以了:
chain.doFilter(req, resp);FilterChain(过滤器链)是用来干什么的呢?这个对象只包含一个void doFilter(ServletRequest request, ServletResponse response)方法,Filter调用此方法去调用下一个web组件(Filter,Servlet等),如果不调用此方法,那么下一个web组件不会被执行。
再放行之前,我们可以在Filter中设置响应头信息,如下:
resp.setContentType("text/html;charset=UTF-8"); chain.doFilter(req, resp);过滤器的过滤过程如下:
<filter> <filter-name>demo</filter-name> <filter-class>cn.zq.filter.DemoServlet</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>name</param-name> <param-value>RiccioZhang</param-value> </init-param> </filter> <filter-mapping> <filter-name>demo</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
然后在init方法中获取:
public void init(FilterConfig config) throws ServletException { System.out.println("DemoServlet.init..."); String encoding = config.getInitParameter("encoding"); String name = config.getInitParameter("name"); System.out.println("encoding="+encoding); System.out.println("name="+name); }
<filter> <!-- filter的名字 --> <filter-name>demo</filter-name> <!-- 类名 --> <filter-class>cn.zq.filter.DemoServlet</filter-class> </filter> <!-- 可以有多个 --> <filter-mapping> <!-- 对哪个filter进行配置 --> <filter-name>demo</filter-name> <!-- 配置过滤的url,不能是/ 其他与servlet配置类似 --> <url-pattern>/*</url-pattern> <!-- 根据名字配置对哪个servlet进行过滤 --> <servlet-name>DemoServlet</servlet-name> <!-- ERROR: <error-page>过来的请求 FORWARD: 对转发过来的请求进行过滤,也就是对request.getRequestDispatcher(path).forward(request, response) INCLUDE:对request.getRequestDispatcher(path).include(request, response)过来的请求进行过滤 REQUEST(默认): 对客户端的请求进行拦截 可以配置多个 --> <dispatcher>ERROR</dispatcher> </filter-mapping>
为了便于编写Filter,本节的所有应用中提供了一个通过的Filter的实现:
package cn.zq.filter; import java.io.IOException; import java.io.Serializable; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * 通用的Filter的实现 * @author zq * */ public abstract class GenericFilter implements Filter, FilterConfig, Serializable{ private static final long serialVersionUID = 5497978960987185665L; private FilterConfig filterConfig; /** * 需要初始化,应该覆盖整个方法 */ public void init(){} public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; init(); } abstract public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; public void destroy() {} public String getFilterName() { return getFilterConfig().getFilterName(); } public String getInitParameter(String name) { return getFilterConfig().getInitParameter(name); } public Enumeration<String> getInitParameterNames() { return getFilterConfig().getInitParameterNames(); } public ServletContext getServletContext() { return getFilterConfig().getServletContext(); } public FilterConfig getFilterConfig() { return filterConfig; } }
package cn.zq.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public abstract class HttpFilter extends GenericFilter{ private static final long serialVersionUID = 1029993995265394412L; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; doFilter(req, resp, chain); } protected abstract void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException; }
第一步:创建Filter
package cn.zq.filter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Iterator; import java.util.Map; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; /** * 处理字符编码的Filter * @author zq * */ public class CharacterEncodingFilter extends HttpFilter{ private static final long serialVersionUID = -4329981031091311164L; private String characterEncoding = "UTF-8"; public void init() { String ce = getInitParameter("characterEncoding"); if(ce != null && !ce.equals("")){ characterEncoding = ce.toUpperCase(); } } public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(characterEncoding); response.setContentType("text/html;charset="+characterEncoding); //优化:只对GET请求的request进行包装 if(request.getMethod().equals("GET")){ request = new ParameterHandlerRequest(request); } chain.doFilter(request, response); } private class ParameterHandlerRequest extends HttpServletRequestWrapper{ public ParameterHandlerRequest(HttpServletRequest request) { super(request); } public String getParameter(String name) { String value = super.getParameter(name); return getString(value); } private String getString(String value){ if(value != null){ try { value = new String( value.getBytes("ISO-8859-1"), getRequest().getCharacterEncoding() ); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return value; } public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if(null != values){ for(int i = 0; i < values.length; i++){ values[i] = getString(values[i]); } } return values; } public Map<String, String[]> getParameterMap() { Map<String, String[]> paramMap = super.getParameterMap(); Iterator<String[]> it = paramMap.values().iterator(); while(it.hasNext()){ String[] values = it.next(); if(null != values){ for(int i = 0; i < values.length; i++){ values[i] = getString(values[i]); } } } return paramMap; } } }
第二步:编写配置文件
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>cn.zq.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
第三步:测试
package cn.zq.servlet; import java.io.IOException; import java.util.Iterator; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class DemoServlet extends HttpServlet { private static final long serialVersionUID = -4363281555738840730L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("--------"+request.getMethod()+"--------"); System.out.println("request = " + request); System.out.println("name = " + request.getParameter("name")); String[] names = request.getParameterValues("name"); if(names != null && names.length > 0){ System.out.println("names[0] = " + names[0]); } Map<String, String[]> parameterMap = request.getParameterMap(); for(Iterator<String> it = parameterMap.keySet().iterator(); it.hasNext();){ String key = it.next(); String[] values = parameterMap.get(key); System.out.println(key+"="+values[0] ); } } }
<servlet> <servlet-name>DemoServlet</servlet-name> <servlet-class>cn.zq.servlet.DemoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DemoServlet</servlet-name> <url-pattern>/servlet/DemoServlet</url-pattern> </servlet-mapping>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <a href="<%=basePath%>servlet/DemoServlet?name=中国">Click</a> <form action="<%=basePath%>servlet/DemoServlet" method="post"> <input type="text" name="name"><br/> <input type="submit"/> </form> </body> </html>
这个过滤器应该被配置在所有过滤器的前面,就能解决全站的乱码了,这样就不用重复的编写解决乱码问题的代码了。
因为jsp页面的有些内容是动态生成的,所有混成jsp页面的意义不大,我们通常会设置这些jsp页面不缓存。
第一步:开发Filter
package cn.zq.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class DynamicPageCacheFilter extends HttpFilter { /** * */ private static final long serialVersionUID = -5449451659530735173L; public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { //设置3个响应头 response.setHeader("pragma", "no-cache"); response.setHeader("cache-control", "no-cache"); response.setDateHeader("expires", 0); chain.doFilter(request, response); } }
<filter> <filter-name>DynamicPageCacheFilter</filter-name> <filter-class> cn.zq.filter.DynamicPageCacheFilter</filter-class> </filter> <filter-mapping> <filter-name>DynamicPageCacheFilter</filter-name> <url-pattern>*.jsp</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping
控制是否缓存,也可以在jsp页面中加入这几个头
<meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0">
第一步:编写Filter
package cn.zq.filter; import java.io.IOException; import java.util.Calendar; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class StaticContentCacheFilter extends HttpFilter{ /** * */ private static final long serialVersionUID = 7660878144738222823L; @Override public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { /* *让图片缓存一个月,html文件缓存一个星期 , *具体的相关信息可以通过配置文件来配置。 */ String requestURI = request.getRequestURI(); long time = 0; int day = 0; if(requestURI.endsWith(".jpg")){ day = 30; }else if(requestURI.endsWith(".html")){ day = 7; } Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DATE, day); time = calendar.getTimeInMillis(); response.setDateHeader("expires", time); chain.doFilter(request, response); } }
第二步:配置
<filter> <filter-name>StaticContentCacheFilter</filter-name> <filter-class>cn.zq.filter.StaticContentCacheFilter</filter-class> </filter> <filter-mapping> <filter-name>StaticContentCacheFilter</filter-name> <url-pattern>*.html</url-pattern> <url-pattern>*.jpg</url-pattern> </filter-mapping>
请求资源,再次请求。查看状态码为304,及缓存文件的日期为N天以后。这是返回的状态码:HTTP/1.1 304 Not Modified
第一步:开发filter
package cn.zq.filter; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * 验证用户是否登录的过滤器 * @author Riccio Zhang * */ public class LoginFilter extends HttpFilter{ /** * */ private static final long serialVersionUID = -6363929637537263967L; protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { HttpSession session = request.getSession(); Object user = session.getAttribute("user"); //没有找到user,则说明用户没有登录,转到登录页面让用户登录 if(user == null){ PrintWriter out = response.getWriter(); out.print("<script>" + "alert('您还未登录!');" + "window.location.href='"+request.getContextPath()+"/login.jsp'" + "</script>"); return; } chain.doFilter(request, response); } }第二步:开发登录功能,配置web.xml
package cn.zq.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginServlet extends HttpServlet { /** * */ private static final long serialVersionUID = 3059445154848670189L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //让用户退出登录 request.getSession().invalidate(); response.sendRedirect(request.getContextPath() + "/login.jsp"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 让用户登录成功 */ String username = request.getParameter("username"); if(username == null || username.length() == 0){ request.setAttribute("msg", "用户名不能为空!"); request.getRequestDispatcher("/login.jsp").forward(request, response); return; } request.getSession().setAttribute("user", username); //重定向到主页 response.sendRedirect(request.getContextPath() + "/page/index.jsp"); } }
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'login.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <p style="color:red;font: 12px;">${requestScope.msg }</p> <form action="<%=path%>/login" method="post"> username : <input type="text" name="username" ><br/> <input type="submit" value="Sign in"> </form> </body> </html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> 欢迎你,<c:out value="${user }"></c:out><br/> <a href="<%=path%>/login">退出</a> </body> </html>
<!-- 对/page/*进行过滤 --> <filter> <filter-name>LoginFilter</filter-name> <filter-class>cn.zq.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/page/*</url-pattern> </filter-mapping> <servlet-mapping> <servlet-name>DemoServlet</servlet-name> <url-pattern>/servlet/DemoServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
在访问/page/index.jsp页面时,未登录是否会跳转到登录页面。登录时显示用户的名字。
自动登录是为了让用户下次访问时,不用输入用户名和密码。将用户的信息保存到cookie中,下次直接从cookie中取。
第一步:开发登录页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'login.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <c:choose> <c:when test="${empty user }"> <p style="color:red;font: 12px;">${requestScope.msg }</p> <form action="<%=path%>/login" method="post"> username : <input type="text" name="username" ><br/> <fieldset> <legend>自动登录</legend> <input type="radio" name="day" value="0">不自动登录<br/> <input type="radio" name="day" value="1">1天<br/> <input type="radio" name="day" value="7">一个星期<br/> <input type="radio" name="day" value="30">一个月<br/> </fieldset> <input type="submit" value="Sign in"> </form> </c:when> <c:otherwise> 欢迎您,<c:out value="${user }"></c:out><br/> <a href="<c:url value='/login' />">退出</a> </c:otherwise> </c:choose> </body> </html>
第二步:开发登录servlet
package cn.zq.servlet; import java.io.IOException; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginServlet extends HttpServlet { /** * */ private static final long serialVersionUID = 3059445154848670189L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //让用户退出登录 request.getSession().invalidate(); //删除cookie Cookie cookie = new Cookie("autoLogin", ""); /** * 0表示删除文件和缓存 * -1表示删除文件,但是还有缓存 */ cookie.setMaxAge(0); cookie.setPath("/"); response.addCookie(cookie); response.sendRedirect(request.getContextPath() + "/login.jsp"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 让用户登录成功 */ String username = request.getParameter("username"); if(username == null || username.length() == 0){ request.setAttribute("msg", "用户名不能为空!"); request.getRequestDispatcher("/login.jsp").forward(request, response); return; } int day = 0; String auto = request.getParameter("day"); try { day = Integer.parseInt(auto); } catch (Exception e) { } //对中文要进行编码 Cookie cookie = new Cookie("autoLogin", URLEncoder.encode(username, request.getCharacterEncoding())); cookie.setMaxAge(day*24*3600); cookie.setPath("/"); response.addCookie(cookie); request.getSession().setAttribute("user", username); response.sendRedirect(request.getContextPath() + "/page/index.jsp"); } }
<servlet-mapping> <servlet-name>DemoServlet</servlet-name> <url-pattern>/servlet/DemoServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
package cn.zq.filter; import java.io.IOException; import java.net.URLDecoder; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class AutoLoginFilter extends HttpFilter{ /** * */ private static final long serialVersionUID = 5891858915933022714L; @Override public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { /* * 优化:当用户手动登录或退出时,就不需要自动登录, * 并且用户已经登录,也不需要自动登录,自动登录的代码是 * 多此一举 */ HttpSession session = request.getSession(); String requestURI = request.getRequestURI(); Object user = session.getAttribute("user"); if(!requestURI.contains("/login") && user == null){ //获取cookie Cookie[] cookies = request.getCookies(); if(cookies != null){ for(Cookie c : cookies){ if("autoLogin".equals(c.getName())){ String username = c.getValue(); username = URLDecoder.decode(username, request.getCharacterEncoding()); session.setAttribute("user", username); break; } } } } chain.doFilter(request, response); } }
<filter> <filter-name>AutoLoginFilter</filter-name> <filter-class>cn.zq.filter.AutoLoginFilter</filter-class> </filter> <filter-mapping> <filter-name>AutoLoginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
在过滤器中,包装HttpServletRequest,修改getParameter方法
package cn.zq.filter; import java.io.IOException; import java.util.Arrays; import java.util.List; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; public class DirtyWordsFilter extends HttpFilter{ private static final long serialVersionUID = -5025789414017693051L; public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { request = new MyHttpServletRequest(request); chain.doFilter(request, response); } } class MyHttpServletRequest extends HttpServletRequestWrapper{ List<String> dirtyWords = Arrays.asList(new String[]{"SB", "sb", "傻B", "2B"}); public MyHttpServletRequest(HttpServletRequest request) { super(request); } public String getParameter(String name) { String value = super.getParameter(name); if(value != null && value.length() > 0){ for(String dw : dirtyWords){ value = value.replaceAll(dw, "***"); } } return value; } }
实现对输出流的压缩:
在tomcat将数据输出到浏览器前,进行压缩,可以减少传送过去的数据,节约成本。如果在流量很少的情况下查看相同的内容和乐而不为呢?
思路:
第一步:编写压缩数据的过滤器
package cn.zq.filter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.zip.GZIPOutputStream; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; public class GzipFilter extends HttpFilter{ /** * */ private static final long serialVersionUID = 3410826595861585118L; public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String ac = request.getHeader("Accept-Encoding"); //支持gzip压缩 if(ac != null && ac.toLowerCase().indexOf("gzip") != -1){ BufferedHttpServletResponse bRes = new BufferedHttpServletResponse(response); chain.doFilter(request, bRes); byte[] data = bRes.getData(); System.out.println("->压缩前数据大小:" + data.length); ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream gout = new GZIPOutputStream(bos); gout.write(data); gout.close(); byte[] compressedData = bos.toByteArray(); System.out.println("->压缩后的数据大小:"+compressedData.length); //设置头信息 response.setContentLength(compressedData.length); response.setHeader("Content-Encoding", "gzip"); ServletOutputStream out = response.getOutputStream(); out.write(compressedData); }else{ chain.doFilter(request, response); } } } class BufferedHttpServletResponse extends HttpServletResponseWrapper{ private ByteArrayOutputStream buf = new ByteArrayOutputStream(); private PrintWriter pw; public BufferedHttpServletResponse(HttpServletResponse response) { super(response); } public PrintWriter getWriter() throws IOException { pw = new PrintWriter( new OutputStreamWriter(buf, getResponse().getCharacterEncoding())); return pw; } public ServletOutputStream getOutputStream() throws IOException { ServletOutputStream sos = new ServletOutputStream() { public void write(int b) throws IOException { buf.write(b); } }; return sos; } public byte[] getData(){ if(pw != null){ pw.close(); } return buf.toByteArray(); } }
<filter> <filter-name>GzipFilter</filter-name> <filter-class>cn.zq.filter.GzipFilter</filter-class> </filter> <filter-mapping> <filter-name>GzipFilter</filter-name> <servlet-name>DemoServlet</servlet-name> <url-pattern>*.jsp</url-pattern> </filter-mapping>
使用压缩过滤器应该注意:应该只用这个压缩过滤器对文本进行压缩,例如jsp,html,css,js等进行压缩,对视频和图片的压缩率很低,不要用来压缩视频和图片,如果是下载,那也不应该用来压缩,这样不但压缩率很低,而且还有可能让服务器奔溃。
关于压缩过滤器的优化:
在doFilter方法中先将数据拿出来,然后放到GzipOutputStream中进行压缩,然后得到压缩后的字节再输出给客户端,这样2次都得到了字节,假如数据量较大,这2次都会占用较多的内存,能不能从包装的response拿出来时直接就是压缩过后的数据呢?改造后的代码如下:
package cn.zq.filter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.zip.GZIPOutputStream; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; public class GzipFilter extends HttpFilter{ /** * */ private static final long serialVersionUID = 3410826595861585118L; public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String ac = request.getHeader("Accept-Encoding"); //支持gzip压缩 if(ac != null && ac.toLowerCase().indexOf("gzip") != -1){ BufferedHttpServletResponse bRes = new BufferedHttpServletResponse(response); chain.doFilter(request, bRes); byte[] compressedData = bRes.getData(); //设置头信息 response.setContentLength(compressedData.length); response.setHeader("Content-Encoding", "gzip"); ServletOutputStream out = response.getOutputStream(); out.write(compressedData); }else{ chain.doFilter(request, response); } } } class BufferedHttpServletResponse extends HttpServletResponseWrapper{ private ByteArrayOutputStream buf = new ByteArrayOutputStream(); private GZIPOutputStream gout; private PrintWriter pw; public BufferedHttpServletResponse(HttpServletResponse response) throws IOException { super(response); gout = new GZIPOutputStream(buf); } public PrintWriter getWriter() throws IOException { pw = new PrintWriter( new OutputStreamWriter(gout, getResponse().getCharacterEncoding())); return pw; } public ServletOutputStream getOutputStream() throws IOException { ServletOutputStream sos = new ServletOutputStream() { public void write(int b) throws IOException { gout.write(b); } }; return sos; } public byte[] getData() throws IOException{ if(pw != null){ pw.close(); } gout.close(); return buf.toByteArray(); } }
本文的例子可以在这里下载”:http://download.csdn.net/detail/qq791967024/8446629
利用Filter能对请求和响应进行预处理,在到达目标组件之前,对强求进行处理,诸如:对请求头和响应头进行处理。充分的利用了包装器设计模式,对request或response进行包装,对其方法进行增强。假如我们拒绝某个请求,就可以写一个过滤器对不希望的请求不放行,即不执行chain.doFilter(request, response)方法,过滤器能帮助我们干很多的事情。