GZip Servlet過濾器可用于GZip壓縮內容從Java Web應用程序發(fā)送到瀏覽器。
為什么要壓縮
Gzip壓縮HTML、js、css等,使得發(fā)送給瀏覽器的數(shù)據(jù)大小變得更小。提升上傳速度,尤其是移動端帶寬受限制的情況下,不過它可能帶來服務器和瀏覽器的CPU消耗問題,但是響應速度會得道很大的改善。
GZip 請求頭
瀏覽器在發(fā)送到HTTP服務器(例如Java Web服務器)的請求中包含Accept-Encoding HTTP標頭。 Accept-Encoding標頭的內容告訴瀏覽器可以接受哪些內容編碼。 如果該標題包含gzip值,則瀏覽器可以接受GZip壓縮內容。 然后服務器可以將GZip壓縮發(fā)送回瀏覽器的內容。
如果從服務器發(fā)回的內容是GZip壓縮的,則服務器會在HTTP響應中包含帶有值gzip的Content-Encoding HTTP標頭。 這樣瀏覽器就知道內容是GZip壓縮的。
為什么使用GZip Servlet過濾器?
如果對每一個Servlet請求都設置壓縮,那肯定在性能上會有差距,所以Gzip Servlet過濾器 可以讓我們對需要壓縮的東西進行壓縮,沒必要壓縮的就不去壓縮。使性能最大化的提升。
GZip Servlet濾波器設計

如圖所示:首先需要一個Servlet過濾器類。 該類映射到web.xml文件中的一組URL。
當一個HTTP請求到達映射到過濾器的Servlet容器時,過濾器會在該請求被Servlet,JSP等處理之前截取請求所針對的目標。 GZip servlet過濾器檢查客戶端(瀏覽器)是否可以接受GZip壓縮內容。如果可以接受,就對目標做壓縮處理,然后將HttpServletResponse對象封裝在GZipServletResponseWrapper進行壓縮,最后將壓縮內容寫入HttpServletResponse。
實例
The code consists of 3 classes. A GZipServletFilter, a GZipServletResponseWrapper and a GZipServletOutputStream.
The GZipServletOutputStream is what compresses the content written to it. It does so by using a GZIPOutputStream internally, which is a standard Java class.
主要用到三個類:GzipServletFilter、GzipServletResponseWrapper、GzipServletOutputStream。
- GZipServletFilter 用來攔截請求,檢查瀏覽器是否接受壓縮。它將在HttpServletResponse傳遞給過濾器鏈之前將信息包裝在GZipServletResponseWrapper中。
- GZipServletResponseWrapper 用來返回一個輸出流給Servlet或者Jsp,可以是GZipServletOutputStream 或者PrintWriter 類型的數(shù)據(jù)。
- GZipServletOutputStream 是用來壓縮并寫入的內容的,通過內部使用GZIPOutputStream來實現(xiàn)。
示例:
public class GZipServletFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if ( acceptsGZipEncoding(httpRequest) ) {
httpResponse.addHeader("Content-Encoding", "gzip");
GZipServletResponseWrapper gzipResponse =
new GZipServletResponseWrapper(httpResponse);
chain.doFilter(request, gzipResponse);
gzipResponse.close();
} else {
chain.doFilter(request, response);
}
}
//判斷 請求對象 是否接受壓縮
private boolean acceptsGZipEncoding(HttpServletRequest httpRequest) {
String acceptEncoding =
httpRequest.getHeader("Accept-Encoding");
return acceptEncoding != null &&
acceptEncoding.indexOf("gzip") != -1;
}
}
class GZipServletResponseWrapper extends HttpServletResponseWrapper {
private GZipServletOutputStream gzipOutputStream = null;
private PrintWriter printWriter = null;
public GZipServletResponseWrapper(HttpServletResponse response)
throws IOException {
super(response);
}
public void close() throws IOException {
//PrintWriter.close does not throw exceptions.
//Hence no try-catch block.
if (this.printWriter != null) {
this.printWriter.close();
}
if (this.gzipOutputStream != null) {
this.gzipOutputStream.close();
}
}
/**
* Flush OutputStream or PrintWriter
*
* @throws IOException
*/
@Override
public void flushBuffer() throws IOException {
//PrintWriter.flush() does not throw exception
if(this.printWriter != null) {
this.printWriter.flush();
}
IOException exception1 = null;
try{
if(this.gzipOutputStream != null) {
this.gzipOutputStream.flush();
}
} catch(IOException e) {
exception1 = e;
}
IOException exception2 = null;
try {
super.flushBuffer();
} catch(IOException e){
exception2 = e;
}
if(exception1 != null) throw exception1;
if(exception2 != null) throw exception2;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (this.printWriter != null) {
throw new IllegalStateException(
"PrintWriter obtained already - cannot get OutputStream");
}
if (this.gzipOutputStream == null) {
this.gzipOutputStream = new GZipServletOutputStream(
getResponse().getOutputStream());
}
return this.gzipOutputStream;
}
@Override
public PrintWriter getWriter() throws IOException {
if (this.printWriter == null && this.gzipOutputStream != null) {
throw new IllegalStateException(
"OutputStream obtained already - cannot get PrintWriter");
}
if (this.printWriter == null) {
this.gzipOutputStream = new GZipServletOutputStream(
getResponse().getOutputStream());
this.printWriter = new PrintWriter(new OutputStreamWriter(
this.gzipOutputStream, getResponse().getCharacterEncoding()));
}
return this.printWriter;
}
@Override
public void setContentLength(int len) {
//ignore, since content length of zipped content
//does not match content length of unzipped content.
}
}
class GZipServletOutputStream extends ServletOutputStream {
private GZIPOutputStream gzipOutputStream = null;
public GZipServletOutputStream(OutputStream output)
throws IOException {
super();
this.gzipOutputStream = new GZIPOutputStream(output);
}
@Override
public void close() throws IOException {
this.gzipOutputStream.close();
}
@Override
public void flush() throws IOException {
this.gzipOutputStream.flush();
}
@Override
public void write(byte b[]) throws IOException {
this.gzipOutputStream.write(b);
}
@Override
public void write(byte b[], int off, int len) throws IOException {
this.gzipOutputStream.write(b, off, len);
}
@Override
public void write(int b) throws IOException {
this.gzipOutputStream.write(b);
}
}
web.xml 配置
為了激活Gzip Servlet Filter,我們需要配置一些東西。
<filter>
<filter-name>GzipFilter</filter-name>
<filter-class>com.xxx.GZipServletFilter</filter-class> #這里填寫自己的GzipServletFilter類路徑。
</filter>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>