【spring】通過GZIP壓縮提高網絡傳輸效率(可以實現任何資源的gzip壓縮、包括AJAX)
gzip是http協議中使用的一種加密算法,客戶端向web服務器端發出了請求後,通常情況下服務器端會將頁面文件和其他資源,返回到客戶端,客戶端加載後渲染呈現,這種情況文件一般都比較大,如果開啓Gzip ,那麼服務器端響應後,會將頁面,JS,CSS等文本文件或者其他文件通過高壓縮算法將其壓縮,然後傳輸到客戶端,由客戶端的瀏覽器負責解壓縮與呈現。通常能節省40%以上的流量(一般都有60%左右),一些PHP,JSP文件也能夠進行壓縮。
1、通過WEB服務器打開GZIP壓縮服務
目前大多數主流WEB中間件都支持GZIP壓縮、下面以Tomcat 爲例進行說明:
找到Tomcat 目錄下的conf下的server.xml,並找到如下信息
將它改成如下的形式(其實在上面代碼的下面已經有了,將他們打開而已。):
這樣,就能夠對html和xml進行壓縮了,如果要壓縮css 和 js,那麼需要將
compressableMimeType=”text/html,text/xml”加入css和js:
一般文本類型的靜態文件可以通過這種方式壓縮後傳輸、提高傳輸效率。
已壓縮過的靜態文件(如圖片)進行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,";