目前主流的浏览器和Web服务器都支持网页的压缩 浏览器和Web服务器对于压缩网页通信过程如下
# 如果浏览器能够接受压缩后的网页内容 那么他会在请求中发送Accept-Encoding请求报头 值为"gzip.deflate" 表明浏览器支持gzip和deflate这两种压缩方式
# Web服务器读取Accept-Encoding请求报头的值来判断浏览器是否接受压缩的内容 如果接受 Web服务器就讲目标页面的响应内容采用gzip压缩方式压缩后再发送到客户端 同时设置Content-Encoding实体报头 值为gzip 以告知浏览器实体正文采用了gzip的压缩编码
# 浏览器接收到响应内容后 根据Content-Encoding实体报头的值对响应内容解压缩 然后显示相应页面的内容
我们可以通过过滤器对目标内容进行压缩 是吸纳原理就是实用包装类对象替换原始响应对象 并使用java.util.zip.GZIPOutputStream作为响应内容的输出流对象 GZIPOutputStream是是过滤流类 他使用GZIP压缩格式写入压缩对象
步骤
1 GZIPServletOutputStream.java
GZIPStreamOutputStream继承自ServletOutputStream 该类的对象用于替换HttpServletResponse.getOutputStream()方法返回的ServletOutputStream对 象 其内部使用GZIPOutputStream的write(int b)方法实现ServletOutputStream类的write(int b)方法 以达到压缩数据的目的
- package filter;
- import java.io.IOException;
- import java.util.zip.GZIPOutputStream;
- import javax.servlet.ServletOutputStream;
- public class GZIPServletOutputStream extends ServletOutputStream
- {
- private GZIPOutputStream gzipos;
- public GZIPServletOutputStream(ServletOutputStream sos) throws IOException
- {
- //使用响应输出流对象构造GZIPOutputStream过滤流对象
- this.gzipos = new GZIPOutputStream(sos);
- }
- @Override
- public void write(int data) throws IOException
- {
- //将写入操作委托给GZIPOutputStream对象的write()方法,从而实现响应输出流的压缩
- gzipos.write(data);
- }
- /**
- * 返回GZIPOutputStream对象,过滤器需要访问这个对象,以便完成将压缩数据写入输出流的操作
- */
- public GZIPOutputStream getGZIPOutputStream()
- {
- return gzipos;
- }
- }
2 CompressionResponseWrapper.java
CompressionResponseWrapper类从HttpServletWrapper类继承 重写了getWriter()和getOutputStream()方法 用GZIPServletOutputStream替换了ServletOutputStream对象
- package filter;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.util.zip.GZIPOutputStream;
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpServletResponseWrapper;
- public class CompressionResponseWrapper extends HttpServletResponseWrapper
- {
- private GZIPServletOutputStream gzipsos;
- private PrintWriter pw;
- public CompressionResponseWrapper(HttpServletResponse response) throws IOException
- {
- super(response);
- //用响应输出流创建GZIPServletOutputStream对象
- gzipsos = new GZIPServletOutputStream(response.getOutputStream());
- ////用GZIPServletOutputStream对象作为参数,构造PrintWriter对象
- pw = new PrintWriter(gzipsos);
- }
- /**
- * 重写setContentLength()方法,以避免Content-Length实体报头所指出的长度
- * 和压缩后的实体正文长度不匹配
- */
- @Override
- public void setContentLength(int len){}
- @Override
- public ServletOutputStream getOutputStream() throws IOException
- {
- return gzipsos;
- }
- @Override
- public PrintWriter getWriter() throws IOException
- {
- return pw;
- }
- /**
- * 过滤器调用这个方法来得到GZIPOutputStream对象,以便完成将压缩数据写入输出流的操作
- */
- public GZIPOutputStream getGZIPOutputStream()
- {
- return gzipsos.getGZIPOutputStream();
- }
- }
3 CompressionFilter.java
是过滤器类 使用CompressionResponseWrapper对象来实现对响应内容的压缩
- package filter;
- import java.io.IOException;
- import java.util.zip.GZIPOutputStream;
- import javax.servlet.Filter;
- import javax.servlet.FilterChain;
- import javax.servlet.FilterConfig;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class CompressionFilter implements Filter
- {
- public void init(FilterConfig filterConfig) throws ServletException{}
- public void destroy(){}
- @Override
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException
- {
- HttpServletRequest httpReq = (HttpServletRequest) request;
- HttpServletResponse httpResp = (HttpServletResponse) response;
- String acceptEncodings = httpReq.getHeader("Accept-Encoding");
- if (acceptEncodings != null && acceptEncodings.indexOf("gzip") > -1)
- {
- // 得到响应对象的封装类对象
- CompressionResponseWrapper respWrapper = new CompressionResponseWrapper(
- httpResp);
- // 设置Content-Encoding实体报头,告诉浏览器实体正文采用了gzip压缩编码
- respWrapper.setHeader("Content-Encoding", "gzip");
- chain.doFilter(httpReq, respWrapper);
- //得到GZIPOutputStream输出流对象
- GZIPOutputStream gzipos = respWrapper.getGZIPOutputStream();
- //调用GZIPOutputStream输出流对象的finish()方法完成将压缩数据写入
- //响应输出流的操作,无须关闭输出流
- gzipos.finish();
- }
- else
- {
- chain.doFilter(httpReq, httpResp);
- }
- }
- }
4 添加CompressionFilter的xml配置
注意 CompressionFilter过滤器的配置应该放在GuestbookFilter过滤器的前面
- <filter>
- <filter-name>CompressionFilter</filter-name>
- <filter-class>filter.CompressionFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>CompressionFilter</filter-name>
- <url-pattern>*.jsp</url-pattern>
- <url-pattern>*.html</url-pattern>
- </filter-mapping>
5 实验测试
看页面感觉不到CompressionFilter过滤器是否起作用了
在命令提示符窗口中输入:
telnet localhost 8080
然后再输入:
- GET /GZIPFilter/index.jsp HTTP/1.1
- Host:localhost
- Accept-Encoding:gzip
连续输入两次回车后 如果看到一堆乱码 就说明CompressionFilter过滤器已经起作用了