压缩过滤器—全站压缩
2017-11-03 本文已影响16人
小小蒜头
前面我介绍的都是Request增强的案例,下面将介绍Response对象的增强。
Servlet API中提供了Response对象的Decorator设计模式实现类HttpServletResponseWrapper,(HttpServletResponseWrapper类实现了Response接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的Response对象对应的方法)以避免用户在对Response对象进行增强时需要实现response接口中的所有方法。
案例:利用过滤器实现全站压缩
思路:
通过Filter向目标页面传递一个自定义的response对象。
- 在自定义的response对象中,重写getOutputStream方法和getWriter方法,使目标资源调用此方法输出页面内容时,获得的是我们自定义的ServletOutputStream对象。
- 在我们自定义的ServletOutputStream对象中,重写write方法,使写出的数据到一个buffer中。
当页面输出完成后,在Filter中就可得到页面写出的数据,从而我们可以调用GzipOutputStream对数据进行压缩后再写出给浏览器,以此完成响应正文件压缩功能。
GZIP压缩:将压缩后的文本文件,发送给浏览器,减少流量。
进行gzip压缩条件:
- 请求头:
Accept-Encoding
:gzip 告诉服务器,该浏览器支持gzip压缩。 - 响应头:
Content-Encoding
: gzip. 告诉浏览器,输出信息用gzip进行压缩了。 - 两个主要类:
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>