GZip Servlet过滤器可用于GZip压缩内容从Java Web应用程序发送到浏览器。
###为什么要压缩
Gzip压缩HTML、js、css等,使得发送给浏览器的数据大小变得更小。提升上传速度,尤其是移动端带宽受限制的情况下,不过它可能带来服务器和浏览器的CPU消耗问题,但是响应速度会得道很大的改善。
###GZip 请求头
浏览器在发送到HTTP服务器(例如Java Web服务器)的请求中包含Accept-Encoding HTTP标头。 Accept-Encoding标头的内容告诉浏览器可以接受哪些内容编码。 如果该标题包含gzip值,则浏览器可以接受GZip压缩内容。 然后服务器可以将GZip压缩发送回浏览器的内容。
如果从服务器发回的内容是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 类型的数据。
- GZipServletOutputStream 是用来压缩并写入的内容的,通过内部使用GZIPOutputStream来实现。
示例:
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,我们需要配置一些东西。
GzipFilter com.xxx.GZipServletFilter #这里填写自己的GzipServletFilter类路径。GzipFilter *.js GzipFilter *.css GzipFilter *.html GzipFilter *.jsp 复制代码 GzipFilter /