全站GZIP压缩过滤的原理及其实现
程序员文章站
2022-03-30 22:57:34
...
在客户端访问数据时候,为了尽可能高效率的传输,在传输的JSP网页的时候,可以采用GZIP压缩的方式,使得网页经过压缩后再去传输。在此,使用过滤器,对发送到的客户端的显示,都先进行一次压缩。然后再显示,具体流程可以参考下图:
也就是说,当每获得一次请求是的时候,通过对getOutputStream的重写,不让其输出到客户端,而是 将其写入到内存字节数组中去。 然后,当需要输出的时候, 也就是过滤器的第二次执行从chain.doFilter(request,response)开始
再次充内存中取出缓存的数据,进行压缩,并用response进行输出。
package cn.Filter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
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.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class FilterGZIP implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest) req;
HttpServletResponse response=(HttpServletResponse) res;
//重新封装response ,来达到改写其已知方法的过程
MyResponse mresponse=new MyResponse(response);
//以上为过滤器的请求时候的过滤过程;
//当请求的时候,传入的response 的getOutPutStream方法已经被修改过了。
//因此,此时如果页面上调用getOutPutStream 方法的时候,其实调用的是自己写的。
chain.doFilter(request, mresponse);
//请求接受, 开始响应,二次输出的时候,就是开始要将内存中的字节数组,经过压缩传输到客户端的过程了。
ByteArrayOutputStream baos =new ByteArrayOutputStream();
GZIPOutputStream gos=new GZIPOutputStream(baos);
//从内存中获取到 原始为压缩的数据。
byte b[]= mresponse.getByte();
System.out.println("压缩前共有"+b.length+"字节");
gos.write(b);
gos.flush();
gos.close();
b=baos.toByteArray();
System.out.println("压缩后共有"+b.length+"字节");
//设置客户端识别打开的方式。
response.setHeader("Content-Encoding", "GZIP");
response.setContentLength(b.length);
response.getOutputStream().write(b);
}
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
以上就是Filter中的全部代码,然后 就是怎样重新封装response的问题了。J2EE中提供了,响应的包装类ServletResponseWrapper 只要继承这个类,重写相应的getOutPutStreaM方法,则目标即刻达成!
class MyResponse extends HttpServletResponseWrapper {
//这个是用来存放页面要输出信息的字节数组。
private ByteArrayOutputStream baos=new ByteArrayOutputStream();
//这个封装的字符流输出对象
private PrintWriter pw;
public MyResponse(HttpServletResponse response) {
super(response);
}
//这个方法,很重要,就是用来获取页面信息存放在内存中的字节数组。 也是压缩的目的
public byte[] getByte() {
try {
if(pw!=null){
pw.flush();
pw.close();
}
baos.flush();
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
//这个方法,就是我们要重写的方法,但是这个方法,有个返回值ServletOutputStream,而这个类又是个抽象类,所以必须还要把这个类给实现了。可以参考下一个代码块
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new MyServletOutputStream(baos);
}
@Override
public PrintWriter getWriter() throws IOException {
pw= new PrintWriter(new OutputStreamWriter(baos,super.getCharacterEncoding()));
return pw;
}
}
//这个是应上面getOutPutStream 的邀请的返回值。 也是要 servlet中实际调用的方法。 不会输出到页面,只会将servlet中要写的内容放入到内存中去。
class MyServletOutputStream extends ServletOutputStream{
//缓冲页面数据存放区域
private ByteArrayOutputStream baos;
public MyServletOutputStream(ByteArrayOutputStream baos){
this.baos=baos;
}
@Override
public void write(int b) throws IOException {
//将输出写入输出缓存
baos.write(b);
}
}
我自己的理解图形
转载于:https://my.oschina.net/anyyang/blog/366216
上一篇: 面试问烂的 Spring AO,全文详解
下一篇: 华为将推出HMS以替代谷歌移动服务GMS
推荐阅读