Java for Web学习笔记(四二):Filter(4)用于压缩

通过Filter实现Http body压缩

  我们将给出一个压缩的例子,解压,加密,解密都可以使用类似的方式。我们先看看运行的效果,通过curl命令发起http请求:

curl -si -H "Accept-Encoding: gzip" http://191.8.1.103:8080/chapter09/servlet


通过Wrapper进行压缩

  对于Filter chain,可能会认为在chain.doFilter()后面进行处理,如压缩,但是虽然处理顺序是这样,但是响应数据可不一定等这个顺序结束后才发送,例如通过writer.flush(),可以强制马上发送。因此,我们采用通过HttpServletResponseWrapper对输出的response进行重新封装,将原始输出转为gzip的压缩输出。

public class CompressionFilter implements Filter {
    @Override
    public void destroy() { }
    
    @Override
    public void init(FilterConfig fConfig) throws ServletException { }

    @Override
    public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException, ServletException{
        if(StringUtils.contains(((HttpServletRequest)request).getHeader("Accept-Encoding"),"gzip")){
            //如果支持gzip压缩方式,进行gzip压缩
            System.out.println("Encoding requested.");
            //(1)设置Header,表明响应进行了gzip压缩
            ((HttpServletResponse)response).setHeader("Content-Encoding", "gzip");
            //(2)通过GzipResponseWrapper(是HttpServletResponseWrapper的继承)对响应response进行重封装,重点是output的重构
            GzipResponseWrapper wrapper =  new GzipResponseWrapper((HttpServletResponse)response);
            try{
                chain.doFilter(request, wrapper);
            }finally{
                try{
                     //(3)Wrapper是对output的处理,需要确保安全的close。这里通过finish来实现。对于输入/输出,原则是谁创建谁关闭,ServletReponse的output(如response.getWriter())是我们代码获取,但不是我们代码创建,其关闭归创建的ServletResponse来处理,我们不需要。但是新的Wrapper中我们对输出进行了重构,需要由我们来确保关闭。
                     wrapper.finish();
                }catch(Exception e){	
                     e.printStackTrace();
                }
            }			
        }else{
            // 不支持gzip的,不进行压缩
            System.out.println("Encoding not requested.");
            chain.doFilter(request, response);
        }		
    }
}

压缩Wrapper的实现

public class GzipResponseWrapper extends HttpServletResponseWrapper{
    //(1)进行输出的格式转换。定义内部静态类,通过将ServletOutputStream的输出放入java.util.zip.GZIPOutputStream后输出。
    //     Java的各类outputStream类似于将内容从一个模子导入另一个模子,每个模子转换格式,而且内容倒出去后,原模子不再有内容。
    //     这里我们将ServletOutputStream倒入到GZIPServletOutputStream,成为了压缩的输出。
    private static class GZIPServletOutputStream extends ServletOutputStream{
        private final ServletOutputStream servletOutputStream;
        private final GZIPOutputStream gzipStream;		

        private GZIPServletOutputStream(ServletOutputStream servletOutputStream) throws IOException{
            this.servletOutputStream = servletOutputStream;
            // 1.1)输出从ServletOutputStream模子倒入java.util.zip.GZIPOutputStream模子
            this.gzipStream = new GZIPOutputStream(servletOutputStream);
        }

        // 1.2)模子变了,相应的状态检测,写,关闭等,应该转到新的模子中
        @Override
        public boolean isReady() {
            return this.servletOutputStream.isReady();
        }

        @Override
        public void setWriteListener(WriteListener writeListener) {
            this.servletOutputStream.setWriteListener(writeListener);			
        }

        @Override
        public void write(int b) throws IOException {
            this.gzipStream.write(b);			
        }

        @Override
        public void close() throws IOException {
            this.gzipStream.close();
        }

        @Override
        public void flush() throws IOException {
            this.gzipStream.flush();
        }
 
        // 1.3)根据GZIPOutputStream的方法,增加finish(),确保关闭该output
        public void finish() throws IOException{
            this.gzipStream.finish();
        }
    }

    // (2)Wrapper的实现 ---------------------
    // 对于HttpServletResponse,可以通过ServletOutputStream写binary,以及通过PrintWriter来写txt,需要对此两者进行封装。
    private GZIPServletOutputStream outputStream;
    private PrintWriter writer;

    public GzipResponseWrapper(HttpServletResponse response) {
        super(response);
    }

    // 2.1)涉及到getOutputStream(),getWriter(),以及将内容输出给client的flushBuffer(),同时增加finish()用于保证关闭。 
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        // Writer是OutputStream的下一个模子,因此有Writer,就没有outputStream,内容已经倒到Writer中了
        if(this.writer != null)
            throw new IllegalStateException("getWriter() already called.");
        if(this.outputStream == null)
            this.outputStream = new GZIPServletOutputStream(super.getOutputStream());
        return this.outputStream;
    }
    @Override
    public PrintWriter getWriter() throws IOException {
        if(this.writer == null && this.outputStream != null)
            throw new IllegalStateException("getOutputStream() already called.");
	
        if(this.writer == null){
            this.outputStream = new GZIPServletOutputStream(super.getOutputStream());
            this.writer = new PrintWriter(new OutputStreamWriter(this.outputStream, this.getCharacterEncoding()));
        }
        return this.writer;
    }
    @Override
    public void flushBuffer() throws IOException {
        if(this.writer != null)
            this.writer.flush();
        else if(this.outputStream != null)
            this.outputStream.flush();
        super.flushBuffer();
    }

    // 2.2)内容压缩,改变body内容,就会改变body的长度,需要对涉及ContentLength进行重构。我们将取消相关的设定,由web contain在发送HTTP响应包时根据实际情况自动给出 
    @Override
    public void setContentLength(int len) { //不进行设置,由web container根据实际情况给出设定
    }
    @Override
    public void setContentLengthLong(long len) {
    }
    @Override
    public void setHeader(String name, String value) {
        if(!name.equalsIgnoreCase("Content-Length"))
            super.setHeader(name, value);
    }
    @Override
    public void addHeader(String name, String value) {
        if(!name.equalsIgnoreCase("Content-Length"))
            super.addHeader(name, value);
    }
    @Override
    public void setIntHeader(String name, int value) {
        if(!name.equalsIgnoreCase("Content-Length"))
            super.setIntHeader(name, value);
    }
    @Override
    public void addIntHeader(String name, int value) {
        if(!name.equalsIgnoreCase("Content-Length"))
            super.addIntHeader(name, value);
    }

    // 2.3)提供finish()确保关闭
    public void finish() throws IOException{
        if(this.writer != null)
            this.writer.close();
        else if(this.outputStream != null)
            this.outputStream.close();
    }
}

相关链接: 我的Professional Java for Web Applications相关文章



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值