web.xml
<!-- 压缩响应内容 -->
<filter>
<filter-name>compressFilter</filter-name>
<filter-class>chapter9.CompressionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>compressFilter</filter-name>
<url-pattern>/compression/*</url-pattern>
</filter-mapping>
过滤器
package chapter9;
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.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
public class CompressionFilter implements Filter
{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
if(((HttpServletRequest)request).getHeader("Accept-Encoding")
.contains("gzip"))
{
System.out.println("Encoding requested.");
((HttpServletResponse)response).setHeader("Content-Encoding", "gzip");
ResponseWrapper wrapper =
new ResponseWrapper((HttpServletResponse)response);
try
{
chain.doFilter(request, wrapper);
}
finally
{
try {
wrapper.finish();
} catch(Exception e) {
e.printStackTrace();
}
}
}
else
{
System.out.println("Encoding not requested.");
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
private static class ResponseWrapper extends HttpServletResponseWrapper
{
private GZIPServletOutputStream outputStream;
private PrintWriter writer;
public ResponseWrapper(HttpServletResponse request)
{
super(request);
}
@Override
public synchronized ServletOutputStream getOutputStream()
throws IOException
{
if(this.writer != null)
throw new IllegalStateException("getWriter() already called.");
if(this.outputStream == null)
this.outputStream =
new GZIPServletOutputStream(super.getOutputStream());
return this.outputStream;
}
@Override
public synchronized PrintWriter getWriter() throws IOException
{
if(this.writer == null && this.outputStream != null)
throw new IllegalStateException(
"getOutputStream() already called.");
if(this.writer == null)
{
this.outputStream =
new GZIPServletOutputStream(super.getOutputStream());
this.writer = new PrintWriter(new OutputStreamWriter(
this.outputStream, this.getCharacterEncoding()
));
}
return this.writer;
}
@Override
public void flushBuffer() throws IOException
{
if(this.writer != null)
this.writer.flush();
else if(this.outputStream != null)
this.outputStream.flush();
super.flushBuffer();
}
@Override
public void setContentLength(int length) { }
@Override
public void setContentLengthLong(long length) { }
@Override
public void setHeader(String name, String value)
{
if(!"content-length".equalsIgnoreCase(name))
super.setHeader(name, value);
}
@Override
public void addHeader(String name, String value)
{
if(!"content-length".equalsIgnoreCase(name))
super.setHeader(name, value);
}
@Override
public void setIntHeader(String name, int value)
{
if(!"content-length".equalsIgnoreCase(name))
super.setIntHeader(name, value);
}
@Override
public void addIntHeader(String name, int value)
{
if(!"content-length".equalsIgnoreCase(name))
super.setIntHeader(name, value);
}
public void finish() throws IOException
{
if(this.writer != null)
this.writer.close();
else if(this.outputStream != null)
this.outputStream.finish();
}
}
private static class GZIPServletOutputStream extends ServletOutputStream
{
private final ServletOutputStream servletOutputStream;
private final GZIPOutputStream gzipStream;
public GZIPServletOutputStream(ServletOutputStream servletOutputStream)
throws IOException
{
this.servletOutputStream = servletOutputStream;
this.gzipStream = new GZIPOutputStream(servletOutputStream);
}
@Override
public boolean isReady()
{
return this.servletOutputStream.isReady();
}
@Override
public void setWriteListener(WriteListener writeListener)
{
this.servletOutputStream.setWriteListener(writeListener);
}
@Override
public void write(int b) throws IOException
{
this.gzipStream.write(b);
}
@Override
public void close() throws IOException
{
this.gzipStream.close();
}
@Override
public void flush() throws IOException
{
this.gzipStream.flush();
}
public void finish() throws IOException
{
this.gzipStream.finish();
}
}
}
首先,该过滤器检查客户端(通常是浏览器)是否在Accept-Encoding请求头中包含"gzip"编码。这是非常重要的检查,因为如果没有该设计,就意味着客户端(通常是浏览器)可能无法处理gzip压缩响应。如果有,过滤器就把Content-Encoding头设置为“gzip”,客户端收到该响应头后,会知道通过"gzip"的方式处理响应。设置完成之后,该过滤器使用私有内部类ResponseWrapper的实例封装响应对象。接下来,该类将使用私有内部类GZIPServletOutputStream封装PrintWriter或者ServletOutputStream,通过它们将数据发送轨道客户端。该封装对象包含了一个java.util.zip.GZIPOutputStream的内部实例。响应数据首先被写入GZIPOutputStream,当请求完成时,它将完成压缩并将压缩响应写入封装的ServletOutputStream中。ResponseWrapper还将阻止Servlet代码设置响应的内容长度头,因为直到响应被压缩之后它才能获得内容长度。