相關知識:javascript
gzip是http協議中使用的一種加密算法,客戶端向web服務器端發出了請求後,一般狀況下服務器端會將頁面文件和其餘資源,返回到客戶端,客戶端加載後渲染呈現,這種狀況文件通常都比較大,若是開啓Gzip ,那麼服務器端響應後,會將頁面,JS,CSS等文本文件或者其餘文件經過高壓縮算法將其壓縮,而後傳輸到客戶端,由客戶端的瀏覽器負責解壓縮與呈現。一般能節省40%以上的流量(通常都有60%左右),一些PHP,JSP文件也可以進行壓縮。css
1.實現: html
Tomcat 開啓Gzip :java
1.找到Tomcat 目錄下的conf下的server.xml,並找到以下信息web
將它改爲以下的形式(其實在上面代碼的下面已經有了,將他們打開而已。):算法
這樣,就可以對html和xml進行壓縮了,若是要壓縮css 和 js,那麼須要將瀏覽器
compressableMimeType=」text/html,text/xml」加入css和js:服務器
你甚至能夠壓縮圖片:網絡
compressableMimeType=」text/html,text/xml」加入css和js:app
開啓後重啓Tomcat ,經過瀏覽器查看headers信息就能看到是否開啓(firebug中有),若是開啓了,那麼transfer-encoding就會是Gzip,不然就是chunked。
2.在代碼級別完成web應用的gzip壓縮的開啓:
1.Wrapper 用來包裝HttpServletResponse 對象
package com.shop.gzip; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; public class Wrapper extends HttpServletResponseWrapper { public static final int OT_NONE = 0, OT_WRITER = 1, OT_STREAM = 2; private int outputType = OT_NONE; private ServletOutputStream output = null; private PrintWriter writer = null; private ByteArrayOutputStream buffer = null; public Wrapper(HttpServletResponse resp) throws IOException { super(resp); buffer = new ByteArrayOutputStream(); } public PrintWriter getWriter() throws IOException { if (outputType == OT_STREAM) throw new IllegalStateException(); else if (outputType == OT_WRITER) return writer; else { outputType = OT_WRITER; writer = new PrintWriter(new OutputStreamWriter(buffer, getCharacterEncoding())); return writer; } } public ServletOutputStream getOutputStream() throws IOException { if (outputType == OT_WRITER) throw new IllegalStateException(); else if (outputType == OT_STREAM) return output; else { outputType = OT_STREAM; output = new WrappedOutputStream(buffer); return output; } } public void flushBuffer() throws IOException { if (outputType == OT_WRITER) writer.flush(); if (outputType == OT_STREAM) output.flush(); } public void reset() { outputType = OT_NONE; buffer.reset(); } public byte[] getResponseData() throws IOException { flushBuffer(); return buffer.toByteArray(); } class WrappedOutputStream extends ServletOutputStream { private ByteArrayOutputStream buffer; public WrappedOutputStream(ByteArrayOutputStream buffer) { this.buffer = buffer; } public void write(int b) throws IOException { buffer.write(b); } public byte[] toByteArray() { return buffer.toByteArray(); } } }
2.過濾器
package com.shop.gzip; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPOutputStream; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class GZipFilter implements Filter { public void destroy() { } /** * 判斷瀏覽器是否支持GZIP * @param request * @return */ private static boolean isGZipEncoding(HttpServletRequest request){ boolean flag=false; String encoding=request.getHeader("Accept-Encoding"); if(encoding.indexOf("gzip")!=-1){ flag=true; } return flag; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse resp = (HttpServletResponse) response; HttpServletRequest req=(HttpServletRequest)request; if(isGZipEncoding(req)){ Wrapper wrapper = new Wrapper(resp); chain.doFilter(request, wrapper); byte[] gzipData = gzip(wrapper.getResponseData()); resp.addHeader("Content-Encoding", "gzip"); resp.setContentLength(gzipData.length); ServletOutputStream output = response.getOutputStream(); output.write(gzipData); output.flush(); } else { chain.doFilter(request, response); } } public void init(FilterConfig arg0) throws ServletException { } private byte[] gzip(byte[] data) { ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(10240); GZIPOutputStream output = null; try { output = new GZIPOutputStream(byteOutput); output.write(data); } catch (IOException e) { } finally { try { output.close(); } catch (IOException e) { } } return byteOutput.toByteArray(); } }
3.在web.xml中配置 GZipFilter,當咱們訪問應用中以.do結尾的資源的使用,服務器端就開啓http gzip壓縮,將壓縮後的信息經過http 協議傳遞給瀏覽器.
<filter> <filter-name>ecsideExport</filter-name> <filter-class>com.web.servlet.GZipFilter</filter-class> </filter> <filter-mapping> <filter-name>ecsideExport</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>
番外:使用Jayson Falkner的過濾器
Jayson Falkner 在他的Two Servlet Filters Every Web Application Should Have(http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html?page=1 ) 裏面介紹了2個提升Java Web Application 效能的Servlet。這裏記一下其中的GZIPFilter。
GZIPFilter能夠用gzip的方式壓縮HTTP Response的內容,從而在Server端加快了響應回覆的速度(gzip壓縮過程需耗用CPU資源,卻大幅減輕了網卡的處理負擔),在Client端縮短了頁面刷新時間,在網絡內減小了數據流量。另外一方面,由於採用的Filter的方式,無需改動應用現有的代碼就可引入該功能。
以上Filter的source code在這裏(http://www.onjava.com/onjava/2003/11/19/examples/jspbook.zip )下載。固然也能夠在這裏(http://www.onjava.com/onjava/2003/11/19/examples/jspbook.jar )下載現成的JAR。
做者定義了3個類來實現這個壓縮功能:GZIPFilter, GZIPResponseWrapper, GZIPResponseStream。類圖大體以下:
這3個類要作的,就是在Server將響應內容裝入HTTP Response以前,先對內容進行GZIP壓縮。其中GZIPFilter實現了javax.servlet.Filter接口,在Java Web Container處理HTTP Response的過程當中即可以掛載使用。另外2個類(GZIPResponseWrapper,GZIPResponseStream)基本上是輔助類,真正的壓縮動做是在GZIPResponseStream中由java.util.zip.GZIPOutputStream來完成的,除此還有其它的一些方法,感受彷佛並無所有用到。這部分還須要另外再研究。
要佈署這個Filter也很簡單。只要將JAR放至應用的library目錄,而且在應用的佈署配置文件web.xml中加入
GZIPFilter
com.jspbook.GZIPFilter
GZIPFilter
/*.jsp
而後啓動Server就能夠了。
GZIPFilter壓縮HTTP Response內容的做用很是明顯。做者在source code的包裏面另外放了TestGZIP.jsp,這是用來顯示GZIPFilter的壓縮效果的JSP。你能夠找一支產生大量內容的目標JSP來測驗一下,就像下面這支show_response_compressed.jsp:
<%@ page import="java.io.*" %> <%@ page import="java.util.zip.*" %> <% String title = "Show Compressed Response"; int size = 100000; out.println("<HTML>/n" + "<HEAD><TITLE>" + title + "</TITLE></HEAD>/n" + "<BODY BGCOLOR=/"#FDF5E6/">/n" + "<H1 ALIGN=/"CENTER/">" + title + " SIZE="+size+"</H1>/n"); String line = "Blah, blah, blah, blah, blah. " + "Yadda, yadda, yadda, yadda."; for(int i=0; i<size; i++) { out.println(line); } out.println("</BODY></HTML>"); %>
運行show_response_compressed.jsp能夠產生相似以下截圖中的頁面。
運行TestGZIP.jsp,在URL中填入show_response_compressed.jsp 所在的地址,提交後便可獲得以下結果。
能夠看到,未經壓縮的HTTP Response數據量達到了2,950,086 bytes (2.9MB),而壓縮後的數據量僅有8,687 bytes(8 KB),壓縮比高達99.7%!
由於是在一臺機器上做的驗證,因此在響應時間的改善方面感受不是很明顯。一樣的,若是是在Intranet環境內,則這種效果也不會很明顯。若是是在Internet上測試,改善的效果應該會比較明顯。
附上源碼
/* * Copyright 2003 Jayson Falkner (jayson@jspinsider.com) * This code is from "Servlets and JavaServer pages; the J2EE Web Tier", * http://www.jspbook.com. You may freely use the code both commercially * and non-commercially. If you like the code, please pick up a copy of * the book and help support the authors, development of more free code, * and the JSP/Servlet/J2EE community. */ package cn.javass.common.web.filter; /*使用gzip優化web應用(filter實現)*/ import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; public class GZIPFilter implements Filter { private static Logger log = LoggerFactory.getLogger(GZIPFilter.class); public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if (req instanceof HttpServletRequest) { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String ae = request.getHeader("accept-encoding"); if (ae != null && ae.indexOf("gzip") != -1) { log.debug("GZIP supported, compressing."); GZIPResponseWrapper wrappedResponse = new GZIPResponseWrapper(response); chain.doFilter(req, wrappedResponse); wrappedResponse.finishResponse(); return; } chain.doFilter(req, res); } } public void init(FilterConfig filterConfig) { // noop } public void destroy() { // noop } }
/* * Copyright 2003 Jayson Falkner (jayson@jspinsider.com) * This code is from "Servlets and JavaServer pages; the J2EE Web Tier", * http://www.jspbook.com. You may freely use the code both commercially * and non-commercially. If you like the code, please pick up a copy of * the book and help support the authors, development of more free code, * and the JSP/Servlet/J2EE community. */ package cn.javass.common.web.filter; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPOutputStream; public class GZIPResponseStream extends ServletOutputStream { protected ByteArrayOutputStream baos = null; protected GZIPOutputStream gzipstream = null; protected boolean closed = false; protected HttpServletResponse response = null; protected ServletOutputStream output = null; public GZIPResponseStream(HttpServletResponse response) throws IOException { super(); closed = false; this.response = response; this.output = response.getOutputStream(); baos = new ByteArrayOutputStream(); gzipstream = new GZIPOutputStream(baos); } @Override public void close() throws IOException { if (closed) { throw new IOException("This output stream has already been closed"); } gzipstream.finish(); byte[] bytes = baos.toByteArray(); // response.addHeader("Content-Length", Integer.toString(bytes.length)); response.addHeader("Content-Encoding", "gzip"); output.write(bytes); output.flush(); output.close(); closed = true; } @Override public void flush() throws IOException { if (closed) { throw new IOException("Cannot flush a closed output stream"); } gzipstream.flush(); } @Override public void write(int b) throws IOException { if (closed) { throw new IOException("Cannot write to a closed output stream"); } gzipstream.write((byte) b); } @Override public void write(byte b[]) throws IOException { write(b, 0, b.length); } @Override public void write(byte b[], int off, int len) throws IOException { if (closed) { throw new IOException("Cannot write to a closed output stream"); } gzipstream.write(b, off, len); } public boolean closed() { return (this.closed); } public void reset() { //noop } }
/* * Copyright 2003 Jayson Falkner (jayson@jspinsider.com) * This code is from "Servlets and JavaServer pages; the J2EE Web Tier", * http://www.jspbook.com. You may freely use the code both commercially * and non-commercially. If you like the code, please pick up a copy of * the book and help support the authors, development of more free code, * and the JSP/Servlet/J2EE community. */ package cn.javass.common.web.filter; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; public class GZIPResponseWrapper extends HttpServletResponseWrapper { protected HttpServletResponse origResponse = null; protected ServletOutputStream stream = null; protected PrintWriter writer = null; public GZIPResponseWrapper(HttpServletResponse response) { super(response); origResponse = response; } public ServletOutputStream createOutputStream() throws IOException { return (new GZIPResponseStream(origResponse)); } public void finishResponse() { try { if (writer != null) { writer.close(); } else { if (stream != null) { stream.close(); } } } catch (IOException e) { } } @Override public void flushBuffer() throws IOException { stream.flush(); } @Override public ServletOutputStream getOutputStream() throws IOException { if (writer != null) { throw new IllegalStateException("getWriter() has already been called!"); } if (stream == null) stream = createOutputStream(); return (stream); } @Override public PrintWriter getWriter() throws IOException { if (writer != null) { return (writer); } if (stream != null) { throw new IllegalStateException("getOutputStream() has already been called!"); } stream = createOutputStream(); writer = new PrintWriter(new OutputStreamWriter(stream, "UTF-8")); return (writer); } @Override public void setContentLength(int length) { } }
web.xml
<!--GZIP文件壓縮的應用 --> <filter> <filter-name>GZIPFilter</filter-name> <filter-class>cn.javass.common.web.filter.GZIPFilter</filter-class> </filter> <filter-mapping> <filter-name>GZIPFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>