您的位置:首页 > 其它

Filter 全站GZIP压缩过滤的原理及其实现

2015-05-07 11:18 585 查看
网上找的两篇不错的response过滤压缩过程与原理

在客户端访问数据时候,为了尽可能高效率的传输,在传输的JSP网页的时候,可以采用GZIP压缩的方式,使得网页经过压缩后再去传输。在此,使用过滤器,对发送到的客户端的显示,都先进行一次压缩。然后再显示,具体流程可以参考下图:





也就是说,当每获得一次请求是的时候,通过对getOutputStream的重写,不让其输出到客户端,而是 将其写入到内存字节数组中去。 然后,当需要输出的时候, 也就是过滤器的第二次执行从chain.doFilter(request,response)开始

再次充内存中取出缓存的数据,进行压缩,并用response进行输出。

?
以上就是Filter中的全部代码,然后 就是怎样重新封装response的问题了。J2EE中提供了,响应的包装类ServletResponseWrapper 只要继承这个类,重写相应的getOutPutStreaM方法,则目标即刻达成!

?
?
我自己的理解图形





*****************************************************************************************************************************

    GZIP压缩:将压缩后的文本文件,发送给浏览器,减少流量。

一、进行gzip压缩条件:

  1、请求头:Accept-Encoding : gzip 告诉服务器,该浏览器支持gzip压缩。

  2、响应头:Content-Encoding : gzip. 告诉浏览器,输出信息用gzip进行压缩了。

  3、两个主要类:

    ByteArrayOutputStream : 内存输出流,还有缓存。

    GZIPOutputStream 包装流;

二、gzip 压缩步骤:

    1、获取字符的字节数组 byte[] buf = str.getBytes() ;

    2、通过GZIPOutputStream 包装流进行输入:

      创建 GZIPOutputStream 输出流时,需要传一个带有缓冲区的输出流,所以我们ByteArrayOutputStream 输出流。而且,ByteArrayOutputStream还可以获取byte[];

    3、将ByteArrayOutputStream 流中的缓存数据,转换成字节数组。

    4、将 压缩后的字节数组通过response 进行输出。不过输出之前要设置Content-Encoding 响应头,value为gzip。告诉浏览器数据进行了gzip压缩,要使用gzip解压。

1             String str = "我是个测试";
2             //1\获取字节数组
3             byte[] bytes = str.getBytes() ;
4
5             System.out.println("压缩前的长度:" + bytes.length);
6             //2\
7             ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
8             GZIPOutputStream  gzip = new GZIPOutputStream(baos) ;
9
10             gzip.write(bytes) ;
11             gzip.close() ;
12             //3\
13             bytes = baos.toByteArray() ;
14             System.out.println("压缩后的长度:" + bytes.length);


    数据较小是,压缩的效果不是很明显,不过数据越大,压缩效果越明显。所以,GZIP压缩一般只处理文本内容,对图片、已经压缩过的文件则不进行压缩。这时就要在配置文件时,配置要过滤的资源。

  

三、GZIPFilter

1 import itheima.decorator.MyHttpServletResponse;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5 import java.util.zip.GZIPOutputStream;
6
7 import javax.servlet.Filter;
8 import javax.servlet.FilterChain;
9 import javax.servlet.FilterConfig;
10 import javax.servlet.ServletException;
11 import javax.servlet.ServletRequest;
12 import javax.servlet.ServletResponse;
13 import javax.servlet.http.HttpServletRequest;
14 import javax.servlet.http.HttpServletResponse;
15 /**
16  * Gzip压缩过滤器
17  * @author 贺佐安
18  *
19  */
20 public class GZIPFilter implements Filter{
21
22     public void init(FilterConfig filterConfig) throws ServletException {
23     }
24
25     public void doFilter(ServletRequest req , ServletResponse resp ,
26             FilterChain chain) throws IOException, ServletException {
27         HttpServletResponse response = (HttpServletResponse) resp ;
28         HttpServletRequest request = (HttpServletRequest) req ;
29         //创建HttpServletResponse 包装类的实例
30         MyHttpServletResponse myResponse = new MyHttpServletResponse(response) ;
31
32         chain.doFilter(request, myResponse) ;
33
34         //GZIP压缩:
35         byte[] buff = myResponse.getBufferedBytes() ;
36         //创建缓存容器:
37         ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
38
39         GZIPOutputStream gzip = new GZIPOutputStream(baos) ;
40
41         gzip.write(buff) ;
42
43         gzip.close() ;
44
45         buff = baos.toByteArray() ;
46
47         //设置响应头;
48         response.setHeader("Content-Encoding", "gzip");
49         response.setContentLength(buff.length) ;
50         response.getOutputStream().write( buff) ;
51     }
52
53     public void destroy() {
54     }
55
56 }


  步骤:

    1、对HttpServletResponse 进行包装 :改写getOutputStream()、getWriter() 方法,并且设置一个临时容器,存储Serlvet处理后要输出的数据。 这里是重点。

1 import java.io.ByteArrayOutputStream;
2 import java.io.IOException;
3 import java.io.OutputStreamWriter;
4 import java.io.PrintWriter;
5
6 import javax.servlet.ServletOutputStream;
7 import javax.servlet.http.HttpServletResponse;
8 import javax.servlet.http.HttpServletResponseWrapper;
9 /**
10  * 对HttpServletResponse 进行包装
11  * @author 贺佐安
12  *
13  */
14 public class MyHttpServletResponse extends HttpServletResponseWrapper {
15     //定义一个容器,用来存储Serlvet 处理完后response 写出的数据
16     private ByteArrayOutputStream bos = new ByteArrayOutputStream()  ;
17     private PrintWriter printWriter = null;
18     public MyHttpServletResponse(HttpServletResponse  response) {
19         super(response) ;
20     }
21     //处理字节流输出的情况
22     public ServletOutputStream getOutputStream() throws IOException {
23         return new MyServletOutputStream(bos);
24     }
25
26     //处理字符流输出的情况:用字符流时要注意乱码:字节转字符要查码表,字符转字节也要查码表
27     public PrintWriter getWriter() throws IOException {
28         printWriter  = new PrintWriter(new OutputStreamWriter(bos, super.getCharacterEncoding())) ;
29         return printWriter;
30     }
31     //获取response 写出的数据
32     public byte[] getBufferedBytes(){
33         try {
34             if (printWriter != null)
35                 printWriter.close() ;
36             bos.flush() ;
37         } catch (IOException e) {
38             e.printStackTrace();
39         }
40         byte[] byteArray = bos.toByteArray() ;
41         return  byteArray;
42     }
43 }


    2、改写getOutputStream 方法时,要返回一个SerlvetOutputStream 类实例,因为SerlvetOutputStream是抽象类,不能创建实例,所以要重写SerlvetOutputStream 类:

1 import java.io.ByteArrayOutputStream;
2 import java.io.IOException;
3
4 import javax.servlet.ServletOutputStream;
5 /**
6  * 包装ServletOutputStream ,改写write 方法。
7  * @author 贺佐安
8  *
9  */
10 public class MyServletOutputStream extends ServletOutputStream {
11     private ByteArrayOutputStream bos = null ;
12     public MyServletOutputStream (ByteArrayOutputStream bos) {
13         this.bos = bos ;
14     }
15     public void write(int b) throws IOException {
16         bos.write(b) ;
17     }
18 }


    3、将包装过的HttpServletResponse 类的实例放行。

    4、然后获取Servlet 处理过后的数据,然后进行Gzip压缩。

    5、调用ServletResponse 的实例,将压缩后的数据写出去。

1     //GZIP压缩:
2         byte[] buff = myResponse.getBufferedBytes() ;
3         //创建缓存容器:
4         ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
5
6         GZIPOutputStream gzip = new GZIPOutputStream(baos) ;
7
8         gzip.write(buff) ;
9
10         gzip.close() ;
11
12         buff = baos.toByteArray() ;
13
14         //设置响应头;
15         response.setHeader("Content-Encoding", "gzip");
16         response.setContentLength(buff.length) ;
17         response.getOutputStream().write( buff) ;


  

  以上便是用Filter 对一些文本资源进行GIZP压缩的步骤。重点就是第二步,如何获取Servlet 返回的数据。更细一点的流程如下图:


    

    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: