ThoughtWorks欧亚创新工作室JavaEE 学习专题技术干货

压缩过滤器—全站压缩

2017-11-03  本文已影响16人  小小蒜头

前面我介绍的都是Request增强的案例,下面将介绍Response对象的增强。

Servlet API中提供了Response对象的Decorator设计模式实现类HttpServletResponseWrapper,(HttpServletResponseWrapper类实现了Response接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的Response对象对应的方法)以避免用户在对Response对象进行增强时需要实现response接口中的所有方法。

案例:利用过滤器实现全站压缩

思路:

通过Filter向目标页面传递一个自定义的response对象。

当页面输出完成后,在Filter中就可得到页面写出的数据,从而我们可以调用GzipOutputStream对数据进行压缩后再写出给浏览器,以此完成响应正文件压缩功能。

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

进行gzip压缩条件:

  1. 请求头:Accept-Encoding :gzip 告诉服务器,该浏览器支持gzip压缩。
  2. 响应头:Content-Encoding: gzip. 告诉浏览器,输出信息用gzip进行压缩了。
  3. 两个主要类:ByteArrayOutputStream: 内存输出流,还有缓存。GZIPOutputStream 包装流;
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;


        //采用增强类传过去
        MyResponse myResponse = new MyResponse(response);
        filterChain.doFilter(request, myResponse);

用增强版的response放行到servlet中去用(让后台把数据 写到 bout中 ),分别写字节流函数的getOutputStream() 和字符流的getWriter()

class MyResponse extends HttpServletResponseWrapper {

        private ByteArrayOutputStream bout = new ByteArrayOutputStream();//往这个流里写数据
        private HttpServletResponse response;
        private PrintWriter pw;

        public MyResponse(HttpServletResponse response) {
            super(response);
            this.response = response;
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException {

            return new MyServletOutputStream(bout);//response.getOutputStream().write("hahah");
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            pw = new PrintWriter(new OutputStreamWriter(bout, response.getCharacterEncoding()));
            return pw;//MyResponse.getWriter("as");
        }

        //数据已经写到bout里,现在需要得到这些数据
        public byte[] getBuffer() {

            if (pw != null) {
                pw.close();
            }
            return bout.toByteArray();
        }
    }

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

class MyServletOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream bout;

        //构造函数接收MyResponse里的bout
        public MyServletOutputStream(ByteArrayOutputStream bout) {
            this.bout = bout;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setWriteListener(WriteListener writeListener) {

        }

        //当调用write方法写数据时,控制数据进bout里
        @Override
        public void write(int b) throws IOException {
            bout.write(b);
        }
    }

然后获取Servlet 处理过后的数据,然后进行Gzip压缩。再调用ServletResponse 的实例,将压缩后的数据写出去。

 //取出缓冲的数据压缩后输出
        byte out[] = myResponse.getBuffer();//得到目标资源的输出

        System.out.println("压缩之前:" + out.length);
        byte gzipout[] = gzip(out);
        System.out.println("压缩之后:" + gzipout.length);
        response.setHeader("content-encoding", "gzip");
        response.setHeader("content-length", gzipout.length + "");

        response.getOutputStream().write(gzipout);

在servlet中进行数据测试:

        String data = "qsadf微博等级开车门,曹禺搜索";
        // response.getOutputStream().write(data.getBytes("UTF-8"));
        response.getWriter().write(data);

web.xml

    <filter>
        <filter-name>GzipFilter</filter-name>
        <filter-class>cn.itcast.filter.GzipFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>GzipFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

源代码:https://github.com/yvettee36/GZIPFilter
上篇:敏感词过滤器

上一篇下一篇

猜你喜欢

热点阅读