1殴瘦、Filter是什么?
Filter 技術(shù)是servlet 2.3 新增加的功能。Filter翻譯過來的意思就是過濾器敛惊,能對客戶端的消息進(jìn)行預(yù)處理,然后將請求轉(zhuǎn)發(fā)給其它的web組件绰更,可以對ServletHttpRequest和ServletHttpResponse進(jìn)行修改和檢查瞧挤。例如:在Filter中可以檢查用戶是否登錄锡宋,對未登錄的用戶跳轉(zhuǎn)到登陸界面。
2特恬、過濾器快速入門
要定義一個(gè)過濾器执俩,則需要實(shí)現(xiàn)javax.servlet.Filter接口,一個(gè)過濾器中包含了三個(gè)與生命周期相關(guān)的方法:
void init(FilterConfig? config)? 過濾器初始化時(shí)執(zhí)行癌刽,F(xiàn)ilterConfig 可以用來獲取過濾器的初始化參數(shù)役首。
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
過濾器過濾請求時(shí)執(zhí)行,包含了request和response妒穴,chain用來是否執(zhí)行下一步請求宋税。
destroy()?? web容器(tomcat)停止時(shí)執(zhí)行
??? 第一步:創(chuàng)建DemoFilter.java
packagecn.zq.filter;importjava.io.IOException;
importjavax.servlet.Filter;
importjavax.servlet.FilterChain;
importjavax.servlet.FilterConfig;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
publicclassDemoServletimplementsFilter{
publicvoidinit(FilterConfig config)throwsServletException{System.out.println("DemoServlet.init...");}publicvoiddoFilter(ServletRequest req, ServletResponse resp, FilterChain chain)throwsIOException, ServletException{
System.out.println("DemoServlet.doFilter..."); ? ? ? ? ? ? ? ?System.out.println("this = "+this); ? ? ? ?}publicvoiddestroy(){System.out.println("DemoServlet.destroy...");}}
第二步:在web.xml文件中添加如下配置:
democn.zq.filter.DemoServletdemo/*
url-pattern配置為/*,表示過濾所有請求讼油。
啟動(dòng)tomcat杰赛,可以看到如下輸出:
說明Filter的init方法在web容器啟動(dòng)的時(shí)候執(zhí)行,讀者可以自行驗(yàn)證destroy()方法會(huì)在web容器停止時(shí)執(zhí)行矮台,訪問主頁:
在訪問主頁的時(shí)候?yàn)g覽器是一片空白的乏屯,控制臺輸出上面的消息,F(xiàn)ilter只會(huì)實(shí)例化一次瘦赫,為什么我們訪問不到我們要訪問的內(nèi)容呢辰晕?只需要在doFilter方法中加入如下的語句就可以了:
chain.doFilter(req, resp);
FilterChain(過濾器鏈)是用來干什么的呢?這個(gè)對象只包含一個(gè)void doFilter(ServletRequest request, ServletResponse response)方法确虱,F(xiàn)ilter調(diào)用此方法去調(diào)用下一個(gè)web組件(Filter,Servlet等)含友,如果不調(diào)用此方法,那么下一個(gè)web組件不會(huì)被執(zhí)行校辩。
再放行之前窘问,我們可以在Filter中設(shè)置響應(yīng)頭信息,如下:
resp.setContentType("text/html;charset=UTF-8");?? ??? ?chain.doFilter(req, resp);
過濾器的過濾過程如下:
過濾器中的各項(xiàng)配置:
配置初始化參數(shù):
democn.zq.filter.DemoServletencodingUTF-8nameRiccioZhangdemo/*
然后在init方法中獲纫酥洹:
publicvoidinit(FilterConfig config)throwsServletException{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的名字 -->demo<!-- 類名 -->cn.zq.filter.DemoServlet<!-- 可以有多個(gè) --><!-- 對哪個(gè)filter進(jìn)行配置 -->demo<!-- ?? 配置過濾的url惠赫,不能是/ ? 其他與servlet配置類似 ? -->/*<!--
? 根據(jù)名字配置對哪個(gè)servlet進(jìn)行過濾
? -->DemoServlet<!--
? ERROR: <error-page>過來的請求
? FORWARD: 對轉(zhuǎn)發(fā)過來的請求進(jìn)行過濾,也就是對request.getRequestDispatcher(path).forward(request, response)
? INCLUDE:對request.getRequestDispatcher(path).include(request, response)過來的請求進(jìn)行過濾
? REQUEST(默認(rèn)): 對客戶端的請求進(jìn)行攔截
? 可以配置多個(gè)
? -->ERROR
3故黑、Filter的應(yīng)用
為了便于編寫Filter儿咱,本節(jié)的所有應(yīng)用中提供了一個(gè)通過的Filter的實(shí)現(xiàn):
packagecn.zq.filter;
importjava.io.IOException;
importjava.io.Serializable;
importjava.util.Enumeration;
importjavax.servlet.Filter;
importjavax.servlet.FilterChain;
importjavax.servlet.FilterConfig;
importjavax.servlet.ServletContext;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
/** * 通用的Filter的實(shí)現(xiàn) *
@authorzq
*
*/
publicabstractclassGenericFilterimplementsFilter,FilterConfig,Serializable{privatestaticfinallongserialVersionUID =5497978960987185665L;
? ? ? ?privateFilterConfig filterConfig;/**
* 需要初始化,應(yīng)該覆蓋整個(gè)方法
*/publicvoidinit(){}publicvoidinit(FilterConfig filterConfig)throwsServletException{this.filterConfig = filterConfig;init();}
abstractpublicvoiddoFilter(ServletRequest request, ServletResponse response,
FilterChain chain)throwsIOException, ServletException;publicvoiddestroy(){}
publicStringgetFilterName(){returngetFilterConfig().getFilterName();}
publicStringgetInitParameter(String name){
returngetFilterConfig().getInitParameter(name);}
publicEnumerationgetInitParameterNames(){
returngetFilterConfig().getInitParameterNames();}
publicServletContextgetServletContext(){returngetFilterConfig().getServletContext();}publicFilterConfiggetFilterConfig(){returnfilterConfig;}}
packagecn.zq.filter;
importjava.io.IOException;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicabstractclassHttpFilterextendsGenericFilter{
privatestaticfinallongserialVersionUID =1029993995265394412L;
publicvoiddoFilter(ServletRequest request, ServletResponse response, FilterChain chain)throwsIOException, ServletException{HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;doFilter(req, resp, chain);}protectedabstractvoiddoFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain)throwsIOException, ServletException;}
3.1场晶、解決GET和POST獲取參數(shù)的亂碼問題
第一步:創(chuàng)建Filter
packagecn.zq.filter;
importjava.io.IOException;
importjava.io.UnsupportedEncodingException;
importjava.util.Iterator;
importjava.util.Map;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletRequestWrapper;
importjavax.servlet.http.HttpServletResponse;
/**
* 處理字符編碼的Filter
*@authorzq
*
*/
publicclassCharacterEncodingFilterextendsHttpFilter{
? ? ? ?privatestaticfinallongserialVersionUID = -4329981031091311164L;privateString characterEncoding ="UTF-8";publicvoidinit(){String ce = getInitParameter("characterEncoding");if(ce !=null&& !ce.equals("")){characterEncoding = ce.toUpperCase();}}publicvoiddoFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)throwsIOException, ServletException{request.setCharacterEncoding(characterEncoding);response.setContentType("text/html;charset="+characterEncoding);//優(yōu)化:只對GET請求的request進(jìn)行包裝if(request.getMethod().equals("GET")){request =newParameterHandlerRequest(request);}chain.doFilter(request, response);}privateclassParameterHandlerRequestextendsHttpServletRequestWrapper{publicParameterHandlerRequest(HttpServletRequest request){super(request);}publicStringgetParameter(String name){String value =super.getParameter(name);returngetString(value);}privateStringgetString(String value){if(value !=null){try{value =newString( value.getBytes("ISO-8859-1"), getRequest().getCharacterEncoding() );}catch(UnsupportedEncodingException e) {e.printStackTrace();}}returnvalue;}publicString[] getParameterValues(String name) {String[] values =super.getParameterValues(name);if(null!= values){for(inti =0; i < values.length; i++){values[i] = getString(values[i]);}}returnvalues;}publicMap getParameterMap() {Map paramMap =super.getParameterMap();Iterator it = paramMap.values().iterator();while(it.hasNext()){String[] values = it.next();if(null!= values){for(inti =0; i < values.length; i++){values[i] = getString(values[i]);}}}returnparamMap;}}}
第二步:編寫配置文件
CharacterEncodingFiltercn.zq.filter.CharacterEncodingFilterCharacterEncodingFilter/*
這個(gè)過濾器應(yīng)該配置在所有過濾器的前面
第三步:測試
packagecn.zq.servlet;
importjava.io.IOException;
importjava.util.Iterator;
importjava.util.Map;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassDemoServletextendsHttpServlet{privatestaticfinallongserialVersionUID = -4363281555738840730L;publicvoiddoGet(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException{?? ??? ?doPost(request, response);?? ?}publicvoiddoPost(HttpServletRequest request, HttpServletResponse response)throwsServletException, 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 parameterMap = request.getParameterMap();for(Iterator it = parameterMap.keySet().iterator();?? ??? ??? ??? ?it.hasNext();){?? ??? ??? ?String key = it.next();?? ??? ??? ?String[] values = parameterMap.get(key);?? ??? ??? ?System.out.println(key+"="+values[0] );?? ??? ?}?? ?}}
DemoServletcn.zq.servlet.DemoServletDemoServlet/servlet/DemoServlet
<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%><%Stringpath=request.getContextPath();StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">My JSP 'index.jsp' starting page<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->Click
訪問并測試:
這個(gè)過濾器應(yīng)該被配置在所有過濾器的前面混埠,就能解決全站的亂碼了,這樣就不用重復(fù)的編寫解決亂碼問題的代碼了诗轻。
3.2钳宪、設(shè)置所有的jsp頁面不緩存
因?yàn)閖sp頁面的有些內(nèi)容是動(dòng)態(tài)生成的,所有混成jsp頁面的意義不大,我們通常會(huì)設(shè)置這些jsp頁面不緩存使套。
第一步:開發(fā)Filter
packagecn.zq.filter;
importjava.io.IOException;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassDynamicPageCacheFilterextendsHttpFilter{/** * */privatestaticfinallongserialVersionUID = -5449451659530735173L;publicvoiddoFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)throwsIOException, ServletException{//設(shè)置3個(gè)響應(yīng)頭response.setHeader("pragma","no-cache");response.setHeader("cache-control","no-cache");response.setDateHeader("expires",0);chain.doFilter(request, response);}}
第二步:配置web.xml
DynamicPageCacheFiltercn.zq.filter.DynamicPageCacheFilterDynamicPageCacheFilter*.jspREQUESTFORWARDINCLUDE
第三步:打開ie,清空所有的緩存鞠柄,cookie侦高,訪問本項(xiàng)目的jsp文件看是否有緩存文件,將Filter拿到厌杜,再訪問看是否有緩存文件奉呛。
控制是否緩存,也可以在jsp頁面中加入這幾個(gè)頭
3.3夯尽、控制靜態(tài)頁面緩存(如html瞧壮,圖片)
第一步:編寫Filter
packagecn.zq.filter;
importjava.io.IOException;
importjava.util.Calendar;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassStaticContentCacheFilterextendsHttpFilter{/** * */privatestaticfinallongserialVersionUID =7660878144738222823L;@OverridepublicvoiddoFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)throwsIOException, ServletException{/*
*讓圖片緩存一個(gè)月,html文件緩存一個(gè)星期 ,
*具體的相關(guān)信息可以通過配置文件來配置匙握。
*/String requestURI = request.getRequestURI();longtime =0;intday =0;if(requestURI.endsWith(".jpg")){day =30;}elseif(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);}}
第二步:配置
StaticContentCacheFiltercn.zq.filter.StaticContentCacheFilterStaticContentCacheFilter*.html*.jpg
第三步:測試
請求資源咆槽,再次請求。查看狀態(tài)碼為304圈纺,及緩存文件的日期為N天以后秦忿。這是返回的狀態(tài)碼:HTTP/1.1 304 Not Modified
3.4 驗(yàn)證用戶是否登錄
第一步:開發(fā)filter
packagecn.zq.filter;
importjava.io.IOException;
importjava.io.PrintWriter;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjavax.servlet.http.HttpSession;
/**
* 驗(yàn)證用戶是否登錄的過濾器
*@authorRiccio Zhang
*
*/
publicclassLoginFilterextendsHttpFilter{
privatestaticfinallongserialVersionUID = -6363929637537263967L;
protectedvoiddoFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)throwsIOException, ServletException{HttpSession session = request.getSession();Object user = session.getAttribute("user");//沒有找到user,則說明用戶沒有登錄蛾娶,轉(zhuǎn)到登錄頁面讓用戶登錄if(user ==null){PrintWriter out = response.getWriter();out.print("<script>"+"alert('您還未登錄!');"+"window.location.href='"+request.getContextPath()+"/login.jsp'"+"</script>");return;}chain.doFilter(request, response);}}
第二步:開發(fā)登錄功能灯谣,配置web.xml
packagecn.zq.servlet;
importjava.io.IOException;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassLoginServletextendsHttpServlet{
privatestaticfinallongserialVersionUID =3059445154848670189L;publicvoiddoGet(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException{//讓用戶退出登錄request.getSession().invalidate();response.sendRedirect(request.getContextPath() +"/login.jsp");}
publicvoiddoPost(HttpServletRequest request, HttpServletResponse response)throwsServletException, 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");}}
登錄頁面:/login.jsp
<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%><%Stringpath=request.getContextPath();StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">My JSP 'login.jsp' starting page${requestScope.msg }username :
登錄成功跳轉(zhuǎn)頁面:/page/index.jsp
<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%><%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%><%Stringpath=request.getContextPath();StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">My JSP 'index.jsp' starting page<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->歡迎你,
退出
<!-- 對/page/*進(jìn)行過濾 -->LoginFiltercn.zq.filter.LoginFilterLoginFilter/page/*DemoServlet/servlet/DemoServletLoginServlet/login
第三步:測試蛔琅,
在訪問/page/index.jsp頁面時(shí)胎许,未登錄是否會(huì)跳轉(zhuǎn)到登錄頁面。登錄時(shí)顯示用戶的名字罗售。
3.5辜窑、自動(dòng)登錄
自動(dòng)登錄是為了讓用戶下次訪問時(shí),不用輸入用戶名和密碼莽囤。將用戶的信息保存到cookie中谬擦,下次直接從cookie中取。
第一步:開發(fā)登錄頁面
<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%><%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%><%Stringpath=request.getContextPath();StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">My JSP 'login.jsp' starting page${requestScope.msg }username :
自動(dòng)登錄不自動(dòng)登錄
1天
一個(gè)星期
一個(gè)月
歡迎您,
退出
第二步:開發(fā)登錄servlet
packagecn.zq.servlet;
importjava.io.IOException;
importjava.net.URLEncoder;
importjavax.servlet.ServletException;
importjavax.servlet.http.Cookie;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassLoginServletextendsHttpServlet{/** * */privatestaticfinallongserialVersionUID =3059445154848670189L;
? ? ? ?publicvoiddoGet(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException{//讓用戶退出登錄request.getSession().invalidate();//刪除cookieCookie cookie =newCookie("autoLogin","");/**
* 0表示刪除文件和緩存
* -1表示刪除文件朽缎,但是還有緩存
*/cookie.setMaxAge(0);cookie.setPath("/");response.addCookie(cookie);response.sendRedirect(request.getContextPath() +"/login.jsp");}
publicvoiddoPost(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException{/*
* 讓用戶登錄成功
*/String username = request.getParameter("username");if(username ==null|| username.length() ==0){request.setAttribute("msg","用戶名不能為空惨远!");request.getRequestDispatcher("/login.jsp").forward(request, response);return;}intday =0;String auto = request.getParameter("day");try{day = Integer.parseInt(auto);}catch(Exception e) {}//對中文要進(jìn)行編碼Cookie cookie =newCookie("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");}}
DemoServlet/servlet/DemoServletLoginServlet/login
第三步:開發(fā)自動(dòng)登錄過濾器
packagecn.zq.filter;
importjava.io.IOException;
importjava.net.URLDecoder;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.http.Cookie;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjavax.servlet.http.HttpSession;
publicclassAutoLoginFilterextendsHttpFilter{
privatestaticfinallongserialVersionUID =5891858915933022714L;
@OverridepublicvoiddoFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)throwsIOException, ServletException{/*
* 優(yōu)化:當(dāng)用戶手動(dòng)登錄或退出時(shí),就不需要自動(dòng)登錄话肖,
* 并且用戶已經(jīng)登錄北秽,也不需要自動(dòng)登錄,自動(dòng)登錄的代碼是
* 多此一舉
*/HttpSession session = request.getSession();String requestURI = request.getRequestURI();Object user = session.getAttribute("user");if(!requestURI.contains("/login") && user ==null){//獲取cookieCookie[] 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);}}
AutoLoginFiltercn.zq.filter.AutoLoginFilterAutoLoginFilter/*
3.6最筒、過濾非法語句(臟話)
在過濾器中贺氓,包裝HttpServletRequest,修改getParameter方法
packagecn.zq.filter;
importjava.io.IOException;
importjava.util.Arrays;
importjava.util.List;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletRequestWrapper;
importjavax.servlet.http.HttpServletResponse;
publicclassDirtyWordsFilterextendsHttpFilter{
privatestaticfinallongserialVersionUID = -5025789414017693051L;
publicvoiddoFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throwsIOException, ServletException{request =newMyHttpServletRequest(request);chain.doFilter(request, response);}}
class
MyHttpServletRequestextendsHttpServletRequestWrapper{List dirtyWords = Arrays.asList(newString[]{"SB","sb","傻B","2B"});publicMyHttpServletRequest(HttpServletRequest request){
? ? ? ? ? ?super(request);}publicStringgetParameter(String name){String value =super.getParameter(name);if(value !=null&& value.length() >0){for(String dw : dirtyWords){value = value.replaceAll(dw,"***");}}returnvalue;}}
3.7、全站壓縮
實(shí)現(xiàn)對輸出流的壓縮:
在tomcat將數(shù)據(jù)輸出到瀏覽器前床蜘,進(jìn)行壓縮辙培,可以減少傳送過去的數(shù)據(jù)蔑水,節(jié)約成本。如果在流量很少的情況下查看相同的內(nèi)容和樂而不為呢扬蕊?
思路:
在調(diào)用request.getOutputStream()或request.getWriter()時(shí)獲取自己的輸出流搀别,將數(shù)據(jù)寫到事先準(zhǔn)備的緩沖中。
在輸出完成后獲取我們自己的緩沖數(shù)據(jù)
然后在對緩沖的數(shù)據(jù)進(jìn)行壓縮尾抑,在過濾器中將數(shù)據(jù)傳輸給瀏覽器
第一步:編寫壓縮數(shù)據(jù)的過濾器
packagecn.zq.filter;
importjava.io.ByteArrayOutputStream;
importjava.io.IOException;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
importjava.util.zip.GZIPOutputStream;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.ServletOutputStream;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjavax.servlet.http.HttpServletResponseWrapper;
publicclassGzipFilterextendsHttpFilter{
privatestaticfinallongserialVersionUID =3410826595861585118L;publicvoiddoFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)throwsIOException, ServletException{String ac = request.getHeader("Accept-Encoding");//支持gzip壓縮if(ac !=null&& ac.toLowerCase().indexOf("gzip") != -1){BufferedHttpServletResponse bRes =newBufferedHttpServletResponse(response);chain.doFilter(request, bRes);byte[] data = bRes.getData();System.out.println("->壓縮前數(shù)據(jù)大行浮:"+ data.length);ByteArrayOutputStream bos =newByteArrayOutputStream();GZIPOutputStream gout =newGZIPOutputStream(bos);gout.write(data);gout.close();byte[] compressedData = bos.toByteArray();System.out.println("->壓縮后的數(shù)據(jù)大小:"+compressedData.length);//設(shè)置頭信息response.setContentLength(compressedData.length);response.setHeader("Content-Encoding","gzip");ServletOutputStream out = response.getOutputStream();out.write(compressedData);}else{chain.doFilter(request, response);}}}
classBufferedHttpServletResponseextendsHttpServletResponseWrapper{privateByteArrayOutputStream buf =newByteArrayOutputStream();
? ? ? ?privatePrintWriter pw;
publicBufferedHttpServletResponse(HttpServletResponse response){
super(response);}publicPrintWritergetWriter()throwsIOException{ ? ?pw =newPrintWriter(newOutputStreamWriter(buf, getResponse().getCharacterEncoding()));returnpw;}
publicServletOutputStreamgetOutputStream()throwsIOException{ServletOutputStream sos =newServletOutputStream() {publicvoidwrite(intb)throwsIOException{buf.write(b);}};returnsos;}
publicbyte[] getData(){if(pw !=null){pw.close();}returnbuf.toByteArray();}}
第二步:配置對所有的jsp進(jìn)行壓縮
GzipFiltercn.zq.filter.GzipFilterGzipFilterDemoServlet*.jsp
第三步:測試壓縮過濾器
使用壓縮過濾器應(yīng)該注意:應(yīng)該只用這個(gè)壓縮過濾器對文本進(jìn)行壓縮再愈,例如jsp,html,css,js等進(jìn)行壓縮榜苫,對視頻和圖片的壓縮率很低,不要用來壓縮視頻和圖片翎冲,如果是下載垂睬,那也不應(yīng)該用來壓縮,這樣不但壓縮率很低府适,而且還有可能讓服務(wù)器奔潰羔飞。
關(guān)于壓縮過濾器的優(yōu)化:
在doFilter方法中先將數(shù)據(jù)拿出來,然后放到GzipOutputStream中進(jìn)行壓縮檐春,然后得到壓縮后的字節(jié)再輸出給客戶端逻淌,這樣2次都得到了字節(jié),假如數(shù)據(jù)量較大疟暖,這2次都會(huì)占用較多的內(nèi)存卡儒,能不能從包裝的response拿出來時(shí)直接就是壓縮過后的數(shù)據(jù)呢?改造后的代碼如下:
packagecn.zq.filter;
importjava.io.ByteArrayOutputStream;
importjava.io.IOException;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
importjava.util.zip.GZIPOutputStream;
importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.ServletOutputStream;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjavax.servlet.http.HttpServletResponseWrapper;
publicclassGzipFilterextendsHttpFilter{
privatestaticfinallongserialVersionUID =3410826595861585118L;publicvoiddoFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)throwsIOException, ServletException{String ac = request.getHeader("Accept-Encoding");//支持gzip壓縮if(ac !=null&& ac.toLowerCase().indexOf("gzip") != -1){BufferedHttpServletResponse bRes =newBufferedHttpServletResponse(response);chain.doFilter(request, bRes);byte[] compressedData = bRes.getData();//設(shè)置頭信息response.setContentLength(compressedData.length);response.setHeader("Content-Encoding","gzip");ServletOutputStream out = response.getOutputStream();out.write(compressedData);}else{chain.doFilter(request, response);}}}
classBufferedHttpServletResponseextendsHttpServletResponseWrapper{privateByteArrayOutputStream buf =newByteArrayOutputStream();
? ? ? ?privateGZIPOutputStream gout;
privatePrintWriter pw;
publicBufferedHttpServletResponse(HttpServletResponse response)throwsIOException{super(response);gout =newGZIPOutputStream(buf);}publicPrintWritergetWriter()throwsIOException{ ? ?pw =newPrintWriter(newOutputStreamWriter(gout, getResponse().getCharacterEncoding()));returnpw;}
publicServletOutputStreamgetOutputStream()throwsIOException{ServletOutputStream sos =newServletOutputStream() {publicvoidwrite(intb)throwsIOException{gout.write(b);}};returnsos;}
publicbyte[] getData()throwsIOException{if(pw !=null){pw.close();}gout.close();returnbuf.toByteArray();}}
4.總結(jié)
利用Filter能對請求和響應(yīng)進(jìn)行預(yù)處理俐巴,在到達(dá)目標(biāo)組件之前骨望,對強(qiáng)求進(jìn)行處理,諸如:對請求頭和響應(yīng)頭進(jìn)行處理欣舵。充分的利用了包裝器設(shè)計(jì)模式擎鸠,對request或response進(jìn)行包裝,對其方法進(jìn)行增強(qiáng)缘圈。假如我們拒絕某個(gè)請求劣光,就可以寫一個(gè)過濾器對不希望的請求不放行,即不執(zhí)行chain.doFilter(request, response)方法糟把,過濾器能幫助我們干很多的事情绢涡。
作者:RiccioZhang
出處:https://blog.csdn.net/ricciozhang/article/details/43833401
源網(wǎng)絡(luò),版權(quán)歸原創(chuàng)者所有遣疯。如有侵權(quán)煩請告知雄可,我們會(huì)立即刪除并表示歉意。
更多技術(shù),歡迎關(guān)注下方公眾號