在代码之前,讲一下用filter实现GZIP压缩的原理:
因为GZIP压缩之后,是从服务器端传输到浏览器端,从servlet到浏览器(从jsp到浏览器),其实是response带回内容,所以我们要在filter中对从servlet端响应回来的response进行操作,因为写内容是response中的writer或者outputStream来完成的,所以我们首先要把response中的writer或者outputStream中的内容取出来,这里我选择了writer进行操作(writer和outputStream其实皆可),但是问题又来了,response.getWriter.write();直接就写出了内容(因为response内部的writer其实是PrintWriter),定位输出到浏览器页面显示上了,并没有缓存起来,那么我们该怎么解决这个问题呢?
思路如下:
使用装饰者模式对response中的getWriter方法进行重写,以便提供一个带缓存的writer,让操作者得到的writer实际上write是定位在writer构造传入的缓存中的(这个缓存可以使用我们之前讲的ByteArrayOutputStream或者CharArrayWriter,这类输出流的好处是,带有一个toByteArray和toCharArray的方法,可以直接获得流内部的内容发)重写HttpServletResponse,同样因为HttpServletResponse只是一个接口,所以要继承自HttpServletResponse的实现类:HttpServletResponseWrapper,(同之前讲过的这篇博文,request和response都是实现wrapper);然后在我们重写之后的response中提供直接获得内部缓存内容的方法,然后我们就可以在filter中正常压缩啦!
servlet中的代码:
package day04; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ContentTestGZIPFilterServlet extends HttpServlet { private static final long serialVersionUID = 3505039773816640152L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); // 准备内容 StringBuffer sb = new StringBuffer(); for (int i=1; i<=3000; i++) { sb.append("abcd"); } System.out.println("压缩前的数据大小:"+sb.toString().getBytes().length); // 输出到浏览器 response.getWriter().write(sb.toString()); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); } }
web.xml中filter的配置:
<filter> <filter-name>GZIPFilter</filter-name> <filter-class>day04.GZIPFilter</filter-class> </filter> <filter-mapping> <filter-name>GZIPFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
filter的代码:
package day04; import java.io.ByteArrayOutputStream; import java.io.CharArrayWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.zip.GZIPOutputStream; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; public class GZIPFilter implements Filter { public void destroy() {} public void init(FilterConfig filterConfig) throws ServletException {} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //1)过滤请求 //创建一个response的装饰者对象 MyHttpResponse myResponse = new MyHttpResponse((HttpServletResponse)response); /** * 放行 */ chain.doFilter(request, myResponse); //3)过滤响应 //从缓存容器对象得到压缩前的内容 //注意:response对象中没有方法获取实体内容,怎么办? char[] content = myResponse.getCharArray(); //gzip压缩 ByteArrayOutputStream buf = new ByteArrayOutputStream(); GZIPOutputStream gzip = new GZIPOutputStream(buf); gzip.write(new String(content).getBytes()); gzip.finish(); byte[] result = buf.toByteArray(); //告诉浏览器发送内容的压缩格式 myResponse.setHeader("content-encoding", "gzip"); //输出: /* * 注意: * 这里的输出不能使用PrintWriter了 * 因为PrintWriter的输出定位已经在了内部的流缓存中了,不能定位到界面输出了 * 所以要使用OutputStream */ response.getOutputStream().write(result); //myRresponse.getWriter().write(new String(result,0,result.length)); } } /** * HttpServletResponse的装饰者类 */ class MyHttpResponse extends HttpServletResponseWrapper{ private HttpServletResponse response; //定义一个缓存容器对象:必须是自带缓冲的流,不能是包装流:即构造方法还需要传入一个writer的流 CharArrayWriter charArray = new CharArrayWriter(); /** * 提供一个获取charArray内容的方法 */ public char[] getCharArray(){ return charArray.toCharArray(); } public MyHttpResponse(HttpServletResponse response) { super(response); this.response = response; } /** * 重写getWriter()方法,让其返回一个带缓存功能的PrintWriter */ @Override public PrintWriter getWriter() throws IOException { /** * 现在已经创建了一个带CharArrayWriter缓存容器的PrintWriter了, * 如果我们调用带缓存PrintWriter对象的write()方法,那么内容会直接写入到CharrArrayWriter缓存容器中。 */ return new PrintWriter(charArray); } }
来源:https://www.cnblogs.com/mzywucai/p/11053413.html