【spring】通过GZIP压缩提高网络传输效率(可以实现任何资源的gzip压缩、包括AJAX)
gzip是http协议中使用的一种加密算法,客户端向web服务器端发出了请求后,通常情况下服务器端会将页面文件和其他资源,返回到客户端,客户端加载后渲染呈现,这种情况文件一般都比较大,如果开启Gzip ,那么服务器端响应后,会将页面,JS,CSS等文本文件或者其他文件通过高压缩算法将其压缩,然后传输到客户端,由客户端的浏览器负责解压缩与呈现。通常能节省40%以上的流量(一般都有60%左右),一些PHP,JSP文件也能够进行压缩。
1、通过WEB服务器打开GZIP压缩服务
目前大多数主流WEB中间件都支持GZIP压缩、下面以Tomcat 为例进行说明:
找到Tomcat 目录下的conf下的server.xml,并找到如下信息
<Connector port =
"8080" maxHttpHeaderSize =
"8192" maxThreads =
"150" minSpareThreads =
"25"
maxSpareThreads =
"75" enableLookups =
"false" redirectPort =
"8443" acceptCount =
"100"
connectionTimeout =
"20000" disableUploadTimeout =
"true"
将它改成如下的形式(其实在上面代码的下面已经有了,将他们打开而已。):
<Connector
port="8080"
maxHttpHeaderSize="8192"
maxThreads="150"
minSpareThreads="25"
maxSpareThreads="75"
enableLookups="false"
redirectPort="8443"
acceptCount="100"
connectionTimeout="20000"
disableUploadTimeout="true"
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml"
>
这样,就能够对html和xml进行压缩了,如果要压缩css 和 js,那么需要将
compressableMimeType=”text/html,text/xml”加入css和js:
<Connector
port="8080"
.........
compressableMimeType="text/html,text/xml,text/css,text/javascript"
>
一般文本类型的静态文件可以通过这种方式压缩后传输、提高传输效率。
已压缩过的静态文件(如图片)进行gzip压缩后大小基本无变化、所以一般不进行压缩。
2、通过过滤器实现gzip压缩
package com.tyyd.framework.web; import java.io.IOException; 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; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import com.tyyd.framework.core.AcwsInfo; import com.tyyd.framework.core.AcwsMonitorLog; import com.tyyd.framework.core.BufferedResponse; import com.tyyd.framework.core.util.ZipUtil; /** * HTTP访问过滤器 */ public class PageVisitFilter2 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //性能监控 long startTime = System.currentTimeMillis(); HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; String uri = request.getRequestURI(); String ext = FilenameUtils.getExtension(uri); try{ response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", -1); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setHeader("renderer", "webkit"); response.setHeader("viewport", "width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0 user-scalable=no"); if(isGZipEncoding(request)){ //需要过滤的扩展名:.htm,.html,.jsp,.js,.ajax,.css String gzippPattern=",.htm,.html,.jsp,.js,.ajax,.css,"; if(StringUtils.indexOf(gzippPattern, ",."+ext+",")!=-1){ BufferedResponse gzipResponse = new BufferedResponse(response); chain.doFilter(request, gzipResponse); byte[] srcData = gzipResponse.getResponseData(); byte[] outData = null; if(srcData.length > 512){ byte[] gzipData = ZipUtil.toGzipBytes(srcData); response.addHeader("Content-Encoding", "gzip"); response.setContentLength(gzipData.length); outData = gzipData; } else { outData = srcData; } ServletOutputStream output = response.getOutputStream(); output.write(outData); output.flush(); } else { chain.doFilter(request, response); } return; } chain.doFilter(request, response); }catch(Exception e){ }finally{ AcwsMonitorLog.warnHttpVisit(startTime, request); } } @Override public void destroy() { } /** * 判断浏览器是否支持GZIP * @param request * @return */ private boolean isGZipEncoding(HttpServletRequest request){ boolean flag=false; String encoding=request.getHeader("Accept-Encoding"); if(encoding.indexOf("gzip")!=-1){ flag=true; } return flag; } }
package com.tyyd.framework.core; 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 BufferedResponse extends HttpServletResponseWrapper { public static final int OT_NONE = 0, OT_WRITER = 1, OT_STREAM = 2; private BufferedOutputStream outputStream = null; private PrintWriter writer = null; private int outputType = OT_NONE; public BufferedResponse(HttpServletResponse response) { super(response); outputStream = new BufferedOutputStream(); } 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(outputStream, getCharacterEncoding()), true); return writer; } } public ServletOutputStream getOutputStream() throws IOException { if (outputType == OT_WRITER) throw new IllegalStateException(); else if (outputType == OT_STREAM) return outputStream; else { outputType = OT_STREAM; return outputStream; } } public void flushBuffer() throws IOException { try{writer.flush();}catch(Exception e){} try{outputStream.flush();}catch(Exception e){} } public void reset() { outputType = OT_NONE; outputStream.reset(); } public byte[] getResponseData() throws IOException { flushBuffer(); return outputStream.toByteArray(); } }
/** * 版权所有: * 项目名称:框架 * 创建者: Wangdf * 创建日期: 2015-2-27 * 文件说明: AJAX 缓存输出流 */ package com.tyyd.framework.core; import java.io.ByteArrayOutputStream; import java.io.IOException; import javax.servlet.ServletOutputStream; public class BufferedOutputStream extends ServletOutputStream { private ByteArrayOutputStream outputStream = null; public BufferedOutputStream(){ outputStream = new ByteArrayOutputStream(1024); } /** * Writes the specified byte to this output stream. The general * contract for <code>write</code> is that one byte is written * to the output stream. The byte to be written is the eight * low-order bits of the argument <code>b</code>. The 24 * high-order bits of <code>b</code> are ignored. * <p> * Subclasses of <code>OutputStream</code> must provide an * implementation for this method. * * @param b the <code>byte</code>. * @exception IOException if an I/O error occurs. In particular, * an <code>IOException</code> may be thrown if the * output stream has been closed. */ public void write(int b) throws IOException { outputStream.write(b); } /** * Writes <code>b.length</code> bytes from the specified byte array * to this output stream. The general contract for <code>write(b)</code> * is that it should have exactly the same effect as the call * <code>write(b, 0, b.length)</code>. * * @param b the data. * @exception IOException if an I/O error occurs. * @see java.io.OutputStream#write(byte[], int, int) */ public void write(byte b[]) throws IOException { outputStream.write(b); } /** * Writes <code>len</code> bytes from the specified byte array * starting at offset <code>off</code> to this output stream. * The general contract for <code>write(b, off, len)</code> is that * some of the bytes in the array <code>b</code> are written to the * output stream in order; element <code>b[off]</code> is the first * byte written and <code>b[off+len-1]</code> is the last byte written * by this operation. * <p> * The <code>write</code> method of <code>OutputStream</code> calls * the write method of one argument on each of the bytes to be * written out. Subclasses are encouraged to override this method and * provide a more efficient implementation. * <p> * If <code>b</code> is <code>null</code>, a * <code>NullPointerException</code> is thrown. * <p> * If <code>off</code> is negative, or <code>len</code> is negative, or * <code>off+len</code> is greater than the length of the array * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @exception IOException if an I/O error occurs. In particular, * an <code>IOException</code> is thrown if the output * stream is closed. */ public void write(byte b[], int off, int len) throws IOException { outputStream.write(b, off, len); } /** * Writes a <code>String</code> to the client, * without a carriage return-line feed (CRLF) * character at the end. * * * @param s the <code>String</code> to send to the client * * @exception IOException if an input or output exception occurred * */ public void print(String s) throws IOException { print(s, "UTF-8"); } public void print(String s, String charsetName) throws IOException { /* * 解决中文乱码问题 */ outputStream.write(s.getBytes(charsetName)); } /** * Writes a <code>boolean</code> value to the client, * with no carriage return-line feed (CRLF) * character at the end. * * @param b the <code>boolean</code> value * to send to the client * * @exception IOException if an input or output exception occurred * */ public void print(boolean b) throws IOException { print(b?"true":"false"); } /** * Writes a character to the client, * with no carriage return-line feed (CRLF) * at the end. * * @param c the character to send to the client * * @exception IOException if an input or output exception occurred * */ public void print(char c) throws IOException { print(String.valueOf(c)); } /** * * Writes an int to the client, * with no carriage return-line feed (CRLF) * at the end. * * @param i the int to send to the client * * @exception IOException if an input or output exception occurred * */ public void print(int i) throws IOException { print(String.valueOf(i)); } /** * * Writes a <code>long</code> value to the client, * with no carriage return-line feed (CRLF) at the end. * * @param l the <code>long</code> value * to send to the client * * @exception IOException if an input or output exception * occurred * */ public void print(long l) throws IOException { print(String.valueOf(l)); } /** * * Writes a <code>float</code> value to the client, * with no carriage return-line feed (CRLF) at the end. * * @param f the <code>float</code> value * to send to the client * * @exception IOException if an input or output exception occurred * * */ public void print(float f) throws IOException { print(String.valueOf(f)); } /** * * Writes a <code>double</code> value to the client, * with no carriage return-line feed (CRLF) at the end. * * @param d the <code>double</code> value * to send to the client * * @exception IOException if an input or output exception occurred * */ public void print(double d) throws IOException { print(String.valueOf(d)); } /** * Writes a carriage return-line feed (CRLF) * to the client. * * * * @exception IOException if an input or output exception occurred * */ public void println() throws IOException { print("\r\n"); } /** * Writes a <code>String</code> to the client, * followed by a carriage return-line feed (CRLF). * * * @param s the <code>String</code> to write to the client * * @exception IOException if an input or output exception occurred * */ public void println(String s){ println(s, "UTF-8"); } public void println(String s, String charsetName){ /* * 解决中文乱码问题 */ try { print(s,charsetName); println(); } catch (IOException e) { throw new RuntimeException(e); } } /** * * Writes a <code>boolean</code> value to the client, * followed by a * carriage return-line feed (CRLF). * * * @param b the <code>boolean</code> value * to write to the client * * @exception IOException if an input or output exception occurred * */ public void println(boolean b) throws IOException { print(b); println(); } /** * * Writes a character to the client, followed by a carriage * return-line feed (CRLF). * * @param c the character to write to the client * * @exception IOException if an input or output exception occurred * */ public void println(char c) throws IOException { print(c); println(); } /** * * Writes an int to the client, followed by a * carriage return-line feed (CRLF) character. * * * @param i the int to write to the client * * @exception IOException if an input or output exception occurred * */ public void println(int i) throws IOException { print(i); println(); } /** * * Writes a <code>long</code> value to the client, followed by a * carriage return-line feed (CRLF). * * * @param l the <code>long</code> value to write to the client * * @exception IOException if an input or output exception occurred * */ public void println(long l) throws IOException { print(l); println(); } /** * * Writes a <code>float</code> value to the client, * followed by a carriage return-line feed (CRLF). * * @param f the <code>float</code> value * to write to the client * * * @exception IOException if an input or output exception * occurred * */ public void println(float f) throws IOException { print(f); println(); } /** * * Writes a <code>double</code> value to the client, * followed by a carriage return-line feed (CRLF). * * * @param d the <code>double</code> value * to write to the client * * @exception IOException if an input or output exception occurred * */ public void println(double d) throws IOException { print(d); println(); } /** * Flushes this output stream and forces any buffered output bytes * to be written out. The general contract of <code>flush</code> is * that calling it is an indication that, if any bytes previously * written have been buffered by the implementation of the output * stream, such bytes should immediately be written to their * intended destination. * <p> * If the intended destination of this stream is an abstraction provided by * the underlying operating system, for example a file, then flushing the * stream guarantees only that bytes previously written to the stream are * passed to the operating system for writing; it does not guarantee that * they are actually written to a physical device such as a disk drive. * <p> * The <code>flush</code> method of <code>OutputStream</code> does nothing. * * @exception IOException if an I/O error occurs. */ public void flush() throws IOException { outputStream.flush(); } /** * Closes this output stream and releases any system resources * associated with this stream. The general contract of <code>close</code> * is that it closes the output stream. A closed stream cannot perform * output operations and cannot be reopened. * <p> * The <code>close</code> method of <code>OutputStream</code> does nothing. * * @exception IOException if an I/O error occurs. */ public void close() throws IOException { outputStream.close(); } /** * Resets the <code>count</code> field of this byte array output * stream to zero, so that all currently accumulated output in the * output stream is discarded. The output stream can be used again, * reusing the already allocated buffer space. * * @see java.io.ByteArrayInputStream#count */ public void reset() { outputStream.reset(); } public byte[] toByteArray() { return outputStream.toByteArray(); } }
在web.xml中配置 PageVisitFilter,当我们访问应用中以.htm,.html,.jsp,.js,.ajax,.css结尾的资源的使用,服务器端就开启http
gzip压缩,将压缩后的信息通过http 协议传递给浏览器.
<filter> <filter-name>Page Visit Filter</filter-name> <filter-class>com.tyyd.framework.web.PageVisitFilter</filter-class> </filter> <filter-mapping> <filter-name>Page Visit Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3、AJAX也可以通过这种方式压缩
只需知道ajax请求的后缀添加到下面的代码中即可:
//需要过滤的扩展名:.htm,.html,.jsp,.js,.ajax,.css
String gzippPattern=",.htm,.html,.jsp,.js,.ajax,.css,";