<Filter>全站GZIP压缩过滤的原理及其实现

☆樱花仙子☆ 提交于 2019-12-10 01:25:26

  在客户端访问数据时候,为了尽可能高效率的传输,在传输的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);
	}
	
}


我自己的理解图形


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!