Java Servlet GZip Servlet Filter 詳解

GZip Servlet過濾器可用于GZip壓縮內容從Java Web應用程序發(fā)送到瀏覽器。

為什么要壓縮

Gzip壓縮HTML、js、css等,使得發(fā)送給瀏覽器的數(shù)據(jù)大小變得更小。提升上傳速度,尤其是移動端帶寬受限制的情況下,不過它可能帶來服務器和瀏覽器的CPU消耗問題,但是響應速度會得道很大的改善。

GZip 請求頭

瀏覽器在發(fā)送到HTTP服務器(例如Java Web服務器)的請求中包含Accept-Encoding HTTP標頭。 Accept-Encoding標頭的內容告訴瀏覽器可以接受哪些內容編碼。 如果該標題包含gzip值,則瀏覽器可以接受GZip壓縮內容。 然后服務器可以將GZip壓縮發(fā)送回瀏覽器的內容。

如果從服務器發(fā)回的內容是GZip壓縮的,則服務器會在HTTP響應中包含帶有值gzip的Content-Encoding HTTP標頭。 這樣瀏覽器就知道內容是GZip壓縮的。

為什么使用GZip Servlet過濾器?

如果對每一個Servlet請求都設置壓縮,那肯定在性能上會有差距,所以Gzip Servlet過濾器 可以讓我們對需要壓縮的東西進行壓縮,沒必要壓縮的就不去壓縮。使性能最大化的提升。

GZip Servlet濾波器設計

image.png

如圖所示:首先需要一個Servlet過濾器類。 該類映射到web.xml文件中的一組URL。
當一個HTTP請求到達映射到過濾器的Servlet容器時,過濾器會在該請求被Servlet,JSP等處理之前截取請求所針對的目標。 GZip servlet過濾器檢查客戶端(瀏覽器)是否可以接受GZip壓縮內容。如果可以接受,就對目標做壓縮處理,然后將HttpServletResponse對象封裝在GZipServletResponseWrapper進行壓縮,最后將壓縮內容寫入HttpServletResponse。

實例

The code consists of 3 classes. A GZipServletFilter, a GZipServletResponseWrapper and a GZipServletOutputStream.

The GZipServletOutputStream is what compresses the content written to it. It does so by using a GZIPOutputStream internally, which is a standard Java class.

主要用到三個類:GzipServletFilter、GzipServletResponseWrapper、GzipServletOutputStream。

  • GZipServletFilter 用來攔截請求,檢查瀏覽器是否接受壓縮。它將在HttpServletResponse傳遞給過濾器鏈之前將信息包裝在GZipServletResponseWrapper中。
  • GZipServletResponseWrapper 用來返回一個輸出流給Servlet或者Jsp,可以是GZipServletOutputStream 或者PrintWriter 類型的數(shù)據(jù)。
  • GZipServletOutputStream 是用來壓縮并寫入的內容的,通過內部使用GZIPOutputStream來實現(xiàn)。

示例:

public class GZipServletFilter implements Filter {

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

  @Override
  public void destroy() {
  }

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

    HttpServletRequest  httpRequest  = (HttpServletRequest)  request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    if ( acceptsGZipEncoding(httpRequest) ) {
      httpResponse.addHeader("Content-Encoding", "gzip");
      GZipServletResponseWrapper gzipResponse =
        new GZipServletResponseWrapper(httpResponse);
      
      chain.doFilter(request, gzipResponse);
      gzipResponse.close();
    } else {
      chain.doFilter(request, response);
    }
  }

//判斷 請求對象 是否接受壓縮
  private boolean acceptsGZipEncoding(HttpServletRequest httpRequest) {
      String acceptEncoding = 
        httpRequest.getHeader("Accept-Encoding");

      return acceptEncoding != null && 
             acceptEncoding.indexOf("gzip") != -1;
  }
}
class GZipServletResponseWrapper extends HttpServletResponseWrapper {

  private GZipServletOutputStream gzipOutputStream = null;
  private PrintWriter             printWriter      = null;

  public GZipServletResponseWrapper(HttpServletResponse response)
          throws IOException {
      super(response);
  }

  public void close() throws IOException {

      //PrintWriter.close does not throw exceptions.
      //Hence no try-catch block.
      if (this.printWriter != null) {
          this.printWriter.close();
      }

      if (this.gzipOutputStream != null) {
          this.gzipOutputStream.close();
      }
  }


  /**
   * Flush OutputStream or PrintWriter
   *
   * @throws IOException
   */

  @Override
  public void flushBuffer() throws IOException {

    //PrintWriter.flush() does not throw exception
    if(this.printWriter != null) {
      this.printWriter.flush();
    }

    IOException exception1 = null;
    try{
      if(this.gzipOutputStream != null) {
        this.gzipOutputStream.flush();
      }
    } catch(IOException e) {
        exception1 = e;
    }

    IOException exception2 = null;
    try {
      super.flushBuffer();
    } catch(IOException e){
      exception2 = e;
    }

    if(exception1 != null) throw exception1;
    if(exception2 != null) throw exception2;
  }

  @Override
  public ServletOutputStream getOutputStream() throws IOException {
    if (this.printWriter != null) {
      throw new IllegalStateException(
        "PrintWriter obtained already - cannot get OutputStream");
    }
    if (this.gzipOutputStream == null) {
      this.gzipOutputStream = new GZipServletOutputStream(
        getResponse().getOutputStream());
    }
    return this.gzipOutputStream;
  }

  @Override
  public PrintWriter getWriter() throws IOException {
     if (this.printWriter == null && this.gzipOutputStream != null) {
       throw new IllegalStateException(
         "OutputStream obtained already - cannot get PrintWriter");
     }
     if (this.printWriter == null) {
       this.gzipOutputStream = new GZipServletOutputStream(
         getResponse().getOutputStream());
       this.printWriter      = new PrintWriter(new OutputStreamWriter(
       this.gzipOutputStream, getResponse().getCharacterEncoding()));
     }
     return this.printWriter;
  }


  @Override
  public void setContentLength(int len) {
    //ignore, since content length of zipped content
    //does not match content length of unzipped content.
  }
}
class GZipServletOutputStream extends ServletOutputStream {
  private GZIPOutputStream    gzipOutputStream = null;

  public GZipServletOutputStream(OutputStream output)
        throws IOException {
    super();
    this.gzipOutputStream = new GZIPOutputStream(output);
  }

  @Override
  public void close() throws IOException {
    this.gzipOutputStream.close();
  }

  @Override
  public void flush() throws IOException {
    this.gzipOutputStream.flush();
  }

  @Override
  public void write(byte b[]) throws IOException {
    this.gzipOutputStream.write(b);
  }

  @Override
  public void write(byte b[], int off, int len) throws IOException {
    this.gzipOutputStream.write(b, off, len);
  }

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

web.xml 配置

為了激活Gzip Servlet Filter,我們需要配置一些東西。

<filter>
  <filter-name>GzipFilter</filter-name>
  <filter-class>com.xxx.GZipServletFilter</filter-class>  #這里填寫自己的GzipServletFilter類路徑。
</filter>

<filter-mapping>
  <filter-name>GzipFilter</filter-name>
  <url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
  <filter-name>GzipFilter</filter-name>
  <url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
  <filter-name>GzipFilter</filter-name>
  <url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
  <filter-name>GzipFilter</filter-name>
  <url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
  <filter-name>GzipFilter</filter-name>
  <url-pattern>/</url-pattern>
</filter-mapping>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容