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

    xiaoxiao2021-03-25  124

    通过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相关文章

    转载请注明原文地址: https://ju.6miu.com/read-11973.html

    最新回复(0)