全站GZIP壓縮過濾的原理及其實現

  在客戶端訪問數據時候,爲了儘量高效率的傳輸,在傳輸的JSP網頁的時候,能夠採用GZIP壓縮的方式,使得網頁通過壓縮後再去傳輸。在此,使用過濾器,對發送到的客戶端的顯示,都先進行一次壓縮。而後再顯示,具體流程能夠參考下圖:java

也就是說,當每得到一次請求是的時候,經過對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方法,則目標即刻達成!app

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);
	}
	
}


我本身的理解圖形
ide

相關文章
相關標籤/搜索