使用filter實現網站的gzip壓縮

kongmd發表於2017-03-23

使用filter實現網站的gzip壓縮

gzip可以提高網站的訪問速度,所以還是有必要開啟的,這裡說說如何用filter實現


直接上程式碼

package com.educate.web.filter;

import com.educate.web.interceptor.LoginInterceptor;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GzipFilter implements Filter {


    private static final Logger LOGGER = LoggerFactory.getLogger(GzipFilter.class);

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

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


        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        if (isGZipEncoding(request)) {


            //需要過濾的副檔名:.htm,.html,.jsp,.js,.ajax,.css,.ftl,.txt, 一般這些已經足夠了,不建議壓縮圖片(具體百度)
            String gzipPattern = ",.htm,.html,.jsp,.js,.ajax,.css,.ftl,.txt,";
            String uri = request.getRequestURI();
            //得到資源的字尾
            String ext = FilenameUtils.getExtension(uri);

            if (StringUtils.indexOf(gzipPattern, ",." + ext + ",") != -1) {

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

                // 從增強版的resp的baout中(存放的是源位元組資料),把資料取出來進行壓縮,
                // 然後再壓縮後的資料用原生的response輸出到客戶端
                ByteArrayOutputStream baout = resp.getBaout();
                byte bs[] = baout.toByteArray();// 源位元組資料
                //太小的不壓縮
                if (bs.length > 512) {

                    LOGGER.debug("過濾的資源路徑:" + uri);
                    LOGGER.debug("壓縮前大小:" + bs.length);

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

                    bs = baout2.toByteArray();
                    LOGGER.debug("壓縮後大小:" + bs.length);

                    // 輸出之前告訴客戶端:我們的資料是gzip格式,然後輸出
                    response.setHeader("Content-Encoding", "gzip");
                    response.setContentLength(bs.length);
                }

                ServletOutputStream output = response.getOutputStream();
                output.write(bs);
                output.flush();
            } else {
                chain.doFilter(request, response);
            }
            return;
        } else {
            chain.doFilter(request, response);
        }


    }

    /**
     * 判斷瀏覽器是否支援GZIP
     *
     * @param request
     * @return
     */
    private boolean isGZipEncoding(HttpServletRequest request) {
        boolean flag = false;
        String encoding = request.getHeader("Accept-Encoding");
        if (encoding.indexOf("gzip") != -1) {
            flag = true;
        }
        return flag;
    }

    @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壓縮 減少網路流量,加快響應速度 -->
    <filter>
        <filter-name>gzip</filter-name>
        <filter-class>com.educate.web.filter.GzipFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>gzip</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

相關文章