实现GZIP压缩过滤器

实现GZIP压缩过滤器

这是书上的例子,为了更好的理解,所以自己敲出来实现一下。

以下为我写的代码,对比书上略有些改动。

GzipResponseStream 类:自定义ServletOutputStream类,使其具有缓存、压缩功能。

应用JDK自带的ByteArrayOutputStream类完成缓存response中的数据。

应用JDK自带的GZIPOutputStream类完成压缩。

public class GzipResponseStream extends ServletOutputStream {
    //用于捕捉内存缓存中的数据,我对它的理解就是可以自动捕捉输出流的数据,并缓存。
    protected ByteArrayOutputStream byteArrayOutputStream = null;
    //JDK中自带的GZIP压缩类
    protected GZIPOutputStream gzipOutputStream = null;
    //接收响应
    protected HttpServletResponse response = null;
    //
    protected ServletOutputStream outputStream = null;

    public GzipResponseStream(HttpServletResponse response) throws IOException {
        super();

        this.response = response;
        this.outputStream = response.getOutputStream();
        byteArrayOutputStream = new ByteArrayOutputStream();
        gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);//将缓存数据放入GZIP流中
        System.out.println("init GzipResponseStream");
    }

    //执行压缩,并将数据输出到浏览器
    public void close() throws IOException {
        /*  if (closed) {
            //初始化后,closed为false不执行if语句抛出异常
            throw new IOException("This output stream has already been closed");
        }*/
        //执行压缩,必须要调用这个方法//api:完成写入压缩数据到输出流不关闭底层流。
        gzipOutputStream.finish();
        //用byte[]接收缓存的数据
        byte[] bytes = byteArrayOutputStream.toByteArray();
        //设置压缩算法为GZIP,浏览器会自动解压数据
        response.addHeader("Content-Length",Integer.toString(bytes.length));
        response.addHeader("Content-Encoding","gzip");
        //将压缩后的数据输出到浏览器中
        outputStream.write(bytes);
        outputStream.flush();
        outputStream.close();
//        closed = true;
    }

    public void flush() throws IOException {
        if (closed) {
            throw new IOException("无法刷新关闭的流");
        }
        gzipOutputStream.flush();
        System.out.println("GzipResponseStream flush");
    }

    //重写write方法,如果流关闭,则抛出异常
    @Override
    public void write(int b) throws IOException {
        /*if (closed) {
            throw new IOException("输出流关闭中");
        }*/
        gzipOutputStream.write((byte)b);
    }

    //重写write方法
    @Override
    public void write(byte[] bytes) throws IOException{
        /*if (closed) {
            throw new IOException("输出流关闭中");
        }*/
        write(bytes);
    }

    //重写write方法
    @Override
    public void write(byte[] bytes, int off, int len) throws IOException{
        if (closed) {
            throw new IOException("输出流关闭中");
        }
        gzipOutputStream.write(bytes,off,len);
    }
    @Override

    public boolean isReady() {
        return false;
    }

    @Override
    public void setWriteListener(WriteListener writeListener) {
    }

    public boolean closed() {
        return this.closed;
    }
}

 

GZIPResponseWrapper类:只进行对输出内容压缩,不进行将内容输出到客户端的操作。因为response不单单是字符内容,还要处理压缩的内容,即二进制内容,所以它需要重写getOutputStream()和getWriter()方法。

public class GZIPResponseWrapper extends HttpServletResponseWrapper {
    //原始的response
    private HttpServletResponse response = null;
    //自定义的outputStream,对数据进行压缩,并且输出
    private GzipResponseStream gzipResponseStream = null;
    //自定义的PrintWriter,将内容输出到ServletResponseStream
    private PrintWriter printWriter = null;

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

    //覆盖getOutputStream方法,处理二进制内容
    @Override
    public GzipResponseStream getOutputStream() throws IOException {
        if (printWriter != null) {
            throw new IllegalStateException("getWriter() har already been called");
        }
        if (gzipResponseStream != null) {
            gzipResponseStream = new GzipResponseStream(response);
            System.out.println("GZIPResponseWrapper getOutputStream");
        }
        return gzipResponseStream;
    }
    //处理字符内容
    @Override
    public PrintWriter getWriter() throws IOException {
        if (printWriter == null) {
            //通过servletOutputStream获得printWriter方法
            if (gzipResponseStream == null) {
                gzipResponseStream = new GzipResponseStream(response);;
            }
            printWriter = new PrintWriter(new OutputStreamWriter(gzipResponseStream, "UTF-8"));
        }
        return (printWriter);
    }
    //执行这个方法对数据进行GZIP压缩,并输出到浏览器
    public void finishResponse() {
        try {
            if (printWriter != null)
                printWriter.close();
            if (gzipResponseStream != null)
                gzipResponseStream.close();
            System.out.println("GzipResponseStream finishResponse");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //刷新ServletOutputStream输出流
    public void flushBuffer() throws IOException {
        gzipResponseStream.flush();
    }
}

GZIPFilter类:通过检查Accept-Encoding标头是否包含gzip字符来判断浏览器是否支持gzip压缩算法,如果支持则进行GZIP压缩,否则直接输出。

@WebFilter(
        description = "内容过滤器",
        filterName = "GZIPFilter",
        urlPatterns = "/*")
public class GZIPFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        if (req instanceof HttpServletRequest) {
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)resp;
            //依据浏览器的Header信息,判断支持的编码方式
            String ae = request.getHeader("Accept-Encoding");
            if (ae != null && ae.toLowerCase().indexOf("gzip") != -1) {
                GZIPResponseWrapper gzipResponseWrapper = new GZIPResponseWrapper(response);
                chain.doFilter(req, gzipResponseWrapper );
                //输出压缩数据
                gzipResponseWrapper.finishResponse();
            }else{
                chain.doFilter(req,resp);
            }
        }
    }
    public void init(FilterConfig config) throws ServletException {
        System.out.println("GZIPFilter init!");
    }
}

以上就是GZIP压缩过滤器的全部代码,改写了一部分,但是不影响其功能。

新增知识:

    1.ByteArrayOutputStream取得输出流中的数据。

    2.GZIPOutputStream用于压缩数据。finish()完成将压缩数据写入输出流的操作,无需关闭底层流。

转载于:https://my.oschina.net/codelx/blog/1036874

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值