JavaWeb-過濾器Filter學習(五)全站壓縮

全站壓縮,最大的好久就是幫客戶端節省流量。
數據壓縮,我們需要用到二個Java類,也就是java.util.zip 中的
類 GZIPOutputStream
此類為使用 GZIP 文件格式寫入壓縮數據實現流過濾器。

java.io
類 ByteArrayOutputStream
此類實現瞭一個輸出流,其中的數據被寫入一個 byte 數組。緩沖區會隨著數據的不斷寫入而自動增長。可使用 toByteArray() 和 toString() 獲取數據。

我們利用GZIPOutputStream(OutputStream out) 使用默認緩沖區大小創建新的輸出流。
再用write(byte[] b)將 b.length 個字節寫入此輸出流。
也就是把數據壓縮後寫入ByteArrayOutputStream。

然後通過內存流輸出到客戶端。

簡單演示:

也就是在一個servlet這樣寫:

public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");

        String str = "網頁壓縮數據hncuhdncu網頁壓縮數據hncuh數據hncuhdncu網頁壓縮數據hncuh數據hncuhdncu網頁壓縮數據hncuhdncu網頁壓縮數據hncuhdncu";

        System.out.println("原大小:" + str.getBytes("utf-8").length);

        // 壓縮輸出--把源數據壓縮輸出到baout內存流中
        ByteArrayOutputStream baout = new ByteArrayOutputStream();

        GZIPOutputStream gout = new GZIPOutputStream(baout);
        gout.write(str.getBytes("utf-8"));
        gout.close();

        // 從baout內存流中把壓縮後的數據取出來,輸出到客戶端
        byte dest[] = baout.toByteArray();
        System.out.println("壓縮後的大小:" + dest.length);

        //輸出之前告訴客戶端:我們的數據是gzip格式,然後輸出
        response.setHeader("Content-Encoding", "gzip");
        response.setContentLength(dest.length);
        OutputStream out = response.getOutputStream();
        out.write(dest);
        out.close();
    }

這樣可以實現壓縮,但是每次我們有一個servlet就要寫一大長串的代碼,很臃腫,也很麻煩,畢竟代碼是一樣的。而且還無法壓縮jsp和html字符文件。

這個時候,我們就需要用到過濾器瞭。隻要攔截所有的servlet和jsp/html就ok。隻要寫一次!很方便。

全站壓縮實例:

index.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>

SecondServlet.java

package cn.hncu.servlets;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SecondServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");
        String str = "網頁壓縮數據hncuhdncu網頁壓縮數據hncuh數據hncuhdncu網頁壓縮數據hncuh數據hncuhdncu網頁壓縮數據hncuhdncu網頁壓縮數據hncuhdncu";
        System.out.println("原大小:" + str.getBytes("utf-8").length);

        OutputStream out = response.getOutputStream();
        out.write(str.getBytes("utf-8"));
        //註意,雖然MyEclipse環境設置的是utf-8編碼,但本句“str.getBytes()”卻是以gbk方式編碼---應該是Tomcat中的JVM采用的方式
    }
}

ThirdServlet.java

package cn.hncu.servlets;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ThirdServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");
        String str="網頁壓縮數據hncuhdncu網頁壓縮數據hncuh數據hncuhdncu網頁壓縮數據hncuh數據hncuhdncu網頁壓縮數據hncuhdncu網頁壓縮數據hncuhdncu";
        System.out.println("原大小:"+ str.getBytes("utf-8").length);

        PrintWriter out = response.getWriter();
        //out.write(str);
        out.println(str);
        out.close();
    }

}

GzipFilter.java

package cn.hncu.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
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.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GzipFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        MyResponse resp = new MyResponse((HttpServletResponse) response);
        // 用增強版的resp放行到servlet中去用(讓後臺把數據 寫到 baout中 )

        chain.doFilter(request, resp);// 放行--讓後臺去寫

        // 從增強版的resp的baout中(存放的是源字節數據),把數據取出來進行壓縮,
        // 然後再壓縮後的數據用原生的response輸出到客戶端
        ByteArrayOutputStream baout = resp.getBaout();
        byte bs[] = baout.toByteArray();// 源字節數據
        System.out.println("壓縮前大小:" + bs.length);

        ByteArrayOutputStream baout2 = new ByteArrayOutputStream();
        GZIPOutputStream gout = new GZIPOutputStream(baout2);
        gout.write(bs);// 把數據壓縮到baout中
        gout.close();

        bs = baout2.toByteArray();
        System.out.println("壓縮後大小:" + bs.length);
        // 輸出之前告訴客戶端:我們的數據是gzip格式,然後輸出
        HttpServletResponse httpResp = (HttpServletResponse) response;
        httpResp.setHeader("Content-Encoding", "gzip");
        httpResp.setContentLength(bs.length);
        OutputStream out = httpResp.getOutputStream();
        out.write(bs);

    }

    @Override
    public void destroy() {
    }

}

class MyResponse extends HttpServletResponseWrapper {
    private ByteArrayOutputStream baout = new ByteArrayOutputStream();

    public MyResponse(HttpServletResponse response) {
        super(response);
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new MyOutputStream(baout);
    }

    public ByteArrayOutputStream getBaout() {
        if(pw!=null){
            pw.flush();
            //這裡很重要,如果不flush或close,不把字符流刷出去,baout中是不會有數據的.
        }
        return baout;
    }

    private PrintWriter pw;
    @Override
    public PrintWriter getWriter() throws IOException {
        pw = new PrintWriter(new OutputStreamWriter(baout, "utf-8"),true);
        return pw;
    }

}

class MyOutputStream extends ServletOutputStream {
    private ByteArrayOutputStream baout = null;

    public MyOutputStream(ByteArrayOutputStream baout) {
        this.baout = baout;
    }

    @Override
    public void write(int b) throws IOException {
        baout.write(b);
    }
}

web.xml:



  

  
    gzip
    cn.hncu.filter.GzipFilter
  
  
    gzip
    /*
  

  
    FirstGzipServlet
    cn.hncu.servlets.FirstGzipServlet
  
  
    SecondServlet
    cn.hncu.servlets.SecondServlet
  
  
    ThirdServlet
    cn.hncu.servlets.ThirdServlet
  



  
    FirstGzipServlet
    /FirstGzipServlet
  
  
    SecondServlet
    /servlet/SecondServlet
  
  
    ThirdServlet
    /servlet/ThirdServlet
      
  
    index.jsp
  

用過濾器來做全站壓縮,無論你怎麼增加servlet,jsp,html,還是照原來的寫,不用你增加代碼,我們隻要在過濾器中對你的數據進行壓縮發送到前臺去就可以瞭!!!

註意,過濾器中用瞭裝飾模式。

完整項目源碼:

https://github.com/chenhaoxiang/Java/tree/master/myGzipWeb

myGzipWeb.zip

發佈留言