GZip Servlet过滤器
GZip Servlet过滤器可用于GZip压缩从Java Web应用程序发送到浏览器的内容。本文将解释其工作原理,并包含一个GZip Servlet过滤器,我们可以在自己的Java Web应用程序中使用它。如果我们不知道什么是Servlet过滤器,请阅读我在Servlet过滤器上的文字。
本教程中显示的GZIP Servlet过滤器使用Java GZIPOutputStream,这在我的Java ZIP教程中已介绍。
为什么GZip压缩内容?
GZip压缩HTML,JavaScript,CSS等使发送到浏览器的数据更小。这样可以加快下载速度。这对于互联网带宽可能受到限制的移动电话尤其有利。 GZip压缩内容增加了服务器和浏览器的CPU开销,但是与未压缩GZip相比,它仍在加快页面总加载速度。
GZip HTTP标头
浏览器在发送到HTTP服务器(例如Java Web服务器)的请求中包含" Accept-Encoding" HTTP标头。 " Accept-Encoding"标头的内容告诉浏览器可以接受哪些内容编码。如果该标头中包含值" gzip",则浏览器可以接受GZip压缩内容。然后,服务器可以GZip压缩发送回浏览器的内容。
如果从服务器发回的内容经过GZip压缩,则服务器会在HTTP响应中包含" Content-Encoding" HTTP标头,其值为" gzip"。这样,浏览器就知道内容是GZip压缩的。
为什么要使用GZip Servlet过滤器?
如果需要,可以在应用程序中的每个Servlet或者JSP中实现GZip压缩。但这变得笨拙。
GZip Servlet过滤器的聪明之处在于,它可以在任何Servlet,JSP甚至静态文件之前和之后执行。这样,我们可以创建单个servlet过滤器,从而为需要它的所有内容启用GZip压缩。 Servlet,JSP等甚至都不知道内容正在压缩,因为它发生在Servlet过滤器中。 GZip Servlet过滤器启用GZip压缩,设置正确的HTTP标头,并确保压缩Servlet,JSP等编写的内容。
GZip Servlet过滤器设计
GZip Servlet过滤器的设计如下所示:
| <img src =“ http://theitroad.local/images/java-servlets/gzip-servlet-filter-1.png” width =“ 600” alt =“ GZip Servlet过滤器设计。” /> |
| GZip Servlet过滤器设计。</ b> |
首先,我们需要一个Servlet过滤器类。该类被映射到web.xml文件中的一组URL。
当HTTP请求到达映射到过滤器的Servlet容器时,过滤器会在请求针对的Servlet,JSP等处理该请求之前对其进行拦截。 GZip Servlet过滤器检查客户端(浏览器)是否可以接受GZip压缩内容。如果是,则启用响应压缩。
通过将HttpServletResponse对象包装在GZipServletResponseWrapper中来启用响应的GZip压缩。该包装器传递给处理请求的Servlet,JSP等。当Servlet,JSP等将输出发送到浏览器时,它会将其发送到响应包装器对象。 Servlet,JSP等无法看到实际的HttpServletResponse和包装对象之间的区别。然后,响应包装对象压缩写入的内容,并将压缩后的内容写入HttpServletResponse。非常简单。
GZip Servlet过滤器代码
这是GZip Servlet过滤器代码。我们编写的方式实际上并不多。这很简单。
该代码包含3个类。 GZipServletFilter,GZipServletResponseWrapper和GZipServletOutputStream。
GZipServletOutputStream是压缩写入其中的内容的方法。它是通过在内部使用" GZIPOutputStream"来实现的,这是一个标准的Java类。
当" GZipServletResponseWrapper"将" OutputStream"或者" PrintWriter"返回给Servlet或者JSP时,它是" GZipServletOutputStream"或者" PrintWriter"写入返回的" GZipServletOutputStream"。
GZipServletFilter是拦截请求,检查客户端是否接受压缩并启用压缩的函数。它是通过将HttpServletResponse封装在GZipServletResponseWrapper中,然后再将其向下传递到过滤器链来实现的。
这是所有三个类:
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);
}
}
GZip Servlet筛选器web.xml配置
为了在Java Web应用程序中激活GZip Servlet过滤器,我们需要以下配置。请记住,用我们自己的GZip Servlet过滤器类的完全限定名称替换类名称。筛选器映射确定激活筛选器的HTTP请求。
<filter> <filter-name>GzipFilter</filter-name> <filter-class>com.myapp.GZipServletFilter</filter-class> </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>

