`

JSP Filter,GZIP压缩响应流

阅读更多
url:http://hi.baidu.com/xhftx/blog/item/fbc11d3012648711ebc4af59.html
关键词:JSP,Filter,Servlet,GZIP

现在主流浏览器都是支持gzip的。服务器压缩网页后进行传输,可减少传输数据的大小使用户感觉访问速度更快。当然,压缩也会消耗一部分服务器处理时间。

用Filter实现对返回信息的压缩,代码参考Tomcat examples里面的
compressionFilters:
GZipStream.java

import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletOutputStream;

public class GZipStream extends ServletOutputStream {

  private GZIPOutputStream zipStream;

  public GZipStream(OutputStream out) throws IOException {
    zipStream = new GZIPOutputStream(out);
  }

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

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

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

  @Override
  public void write(int arg0) throws IOException {
    zipStream.write(arg0);
  }

  public void finish() throws IOException {
    zipStream.finish();
  }

  public void close() throws IOException {
    zipStream.close();
  }

} 


GZipResponse.java

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

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GZipResponse extends HttpServletResponseWrapper {
  private GZipStream stream;
  private PrintWriter writer;
  public GZipResponse(HttpServletResponse response) throws IOException{
    super(response);
    stream=new GZipStream(response.getOutputStream());
  }
  
  @Override
  public ServletOutputStream getOutputStream() throws IOException {
    return stream;
  }

  @Override
  public PrintWriter getWriter() throws IOException {
    if (writer == null) {
      writer = new PrintWriter(new OutputStreamWriter(
          getOutputStream(), getCharacterEncoding()));
    }
    return writer;
  }

  public void flush() throws IOException {
    if (writer != null) {
      writer.flush();
    }
    stream.finish();
  }

} 


GZipFilter.java


import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GZipFilter implements Filter {

  public void destroy() {
  }

  public void init(FilterConfig fConfig) throws ServletException {
  }

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
  throws IOException, ServletException {
    HttpServletRequest req=(HttpServletRequest)request;
    HttpServletResponse res=(HttpServletResponse)response;
    if(isGZipEncoding(req)){
      GZipResponse zipResponse=new GZipResponse(res);
      res.setHeader("Content-Encoding", "gzip");
      chain.doFilter(request, zipResponse);
      zipResponse.flush();
    } else {
      chain.doFilter(request, response);
    }
  }
  
  /**
   * 判断浏览器是否支持GZIP
   * @param request
   * @return
   */
  private static boolean isGZipEncoding(HttpServletRequest request){
    boolean flag=false;
    String encoding=request.getHeader("Accept-Encoding");
    if(encoding.indexOf("gzip")!=-1){
      flag=true;
    }
    return flag;
  }

} 

web.xml配置
<filter>
   <description>
   </description>
   <display-name>GZipFilter</display-name>
   <filter-name>GZipFilter</filter-name>
   <filter-class>GZipFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>GZipFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>


这个配置是对所有的资源都进行压缩传输,对于图片,flash等本身已经压缩过的文件就没有必要再进行压缩了,可以根据自己的需要更改<url-pattern>已提高WEB访问速度。

tomcat自带源码剖析:
url:http://hi.baidu.com/springfieldx/blog/item/9faa88dfd5760414495403b6.html
在响应请求的时候对response进行封装,替换他的输出流为 GZipOutputStream 压缩输出流


package compressionFilters;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;


public class CompressionResponseStream extends ServletOutputStream {

//是否启用压缩的临界值
protected int compressThreshold = 0;

//临时容纳写入的数据缓冲区
protected byte[] buffer = null;

//缓冲区实际写入的数据量
protected int bufferCount = 0;

protected GZIPOutputStream gzipstream = null;

//当前流对象是否处于关闭状态
protected boolean closed = false;

protected int length = -1;

//用于返回数据的 Response对象
protected HttpServletResponse response = null;

protected ServletOutputStream output = null;



public CompressionResponseStream(HttpServletResponse response) throws IOException{
   super();
   closed = false;
   this.response = response;
   this.output = response.getOutputStream();
}

//设置启用压缩的临界值,并初始化输出缓冲区
public void setBuffer(int threshold){
   compressThreshold = threshold;
   buffer = new byte[threshold];
}

/**
* 关闭流对象
*/
public void close() throws IOException{
   if(closed){
    throw new IOException("This output stream has already been closed");
   }
  
   //如果当前启用的是压缩流,用压缩流刷新缓冲区
   if(gzipstream != null){
    flushToGZip();
    gzipstream.close();
    gzipstream = null;
   }else{
    //如果未开启压缩,则用response的默认输出流刷新缓冲区
    if(bufferCount > 0){
     output.write(buffer, 0, bufferCount);
     bufferCount = 0;
    }
   }
}

/**
* 刷新输出缓冲区
*/
public void flush() throws IOException{
   if(closed){
    throw new IOException("Cannot flush a closed output stream");
   }
   if(gzipstream != null){
    gzipstream.flush();
   }
}

public void write(int b) throws IOException {
  
   if(closed){
    throw new IOException("Cannot write to a closed output stream");
   }
  
   //如果数据超出了缓冲区(启用压缩的临界值),则启用压缩模式
   if(bufferCount >= buffer.length){
    flushToGZip();
   }
   //如果没有超出缓冲区,则将数据写入缓冲区
   buffer[bufferCount++] = (byte)b;
  
}

public void write(byte[] b,int off,int len) throws IOException{
   if(closed){
    throw new IOException("Cannot write to a closed output stream");
   }
  
   if(len == 0){
    return;
   }
   //如果缓冲区能容纳这些数据则写入缓冲区
   if(len <= buffer.length - bufferCount){
    System.arraycopy(b, off, buffer, bufferCount, len);
    bufferCount += len;
    return;
   }
  
   //如果缓冲区剩余空间不住足,则开启压缩流对象
   flushToGZip();
  
   //如果清空后的缓冲区能够容纳传送过来的数据,则将数据写入缓冲区
   if(len <= buffer.length - bufferCount){
    System.arraycopy(b, off, buffer, bufferCount, len);
    bufferCount += len;
    return;
   }
  
   //如果缓冲区不能容纳传送进来的数据,则直接将数据写入压缩流对象
   writeToGZip(b, off, len);
}


//刷新压缩流对象的缓冲区
public void flushToGZip() throws IOException{
   if(bufferCount > 0){
    writeToGZip(buffer, 0, bufferCount);
    bufferCount = 0;
   }
}

public void writeToGZip(byte b[],int off,int len)throws IOException{
  
   //如果压缩流对象为空,这里初始化它
   if(gzipstream == null){
    response.addHeader("Content-Encoding", "gzip");
    gzipstream = new GZIPOutputStream(output);
   }
  
   //将数据听过压缩流对象输出到response的输出流
   gzipstream.write(b,off,len);
}



public boolean closed(){
   return closed;
}

}



package compressionFilters;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;


public class CompressionResponseStream extends ServletOutputStream {

//是否启用压缩的临界值
protected int compressThreshold = 0;

//临时容纳写入的数据缓冲区
protected byte[] buffer = null;

//缓冲区实际写入的数据量
protected int bufferCount = 0;

protected GZIPOutputStream gzipstream = null;

//当前流对象是否处于关闭状态
protected boolean closed = false;

protected int length = -1;

//用于返回数据的 Response对象
protected HttpServletResponse response = null;

protected ServletOutputStream output = null;



public CompressionResponseStream(HttpServletResponse response) throws IOException{
   super();
   closed = false;
   this.response = response;
   this.output = response.getOutputStream();
}

//设置启用压缩的临界值,并初始化输出缓冲区
public void setBuffer(int threshold){
   compressThreshold = threshold;
   buffer = new byte[threshold];
}

/**
* 关闭流对象
*/
public void close() throws IOException{
   if(closed){
    throw new IOException("This output stream has already been closed");
   }
  
   //如果当前启用的是压缩流,用压缩流刷新缓冲区
   if(gzipstream != null){
    flushToGZip();
    gzipstream.close();
    gzipstream = null;
   }else{
    //如果未开启压缩,则用response的默认输出流刷新缓冲区
    if(bufferCount > 0){
     output.write(buffer, 0, bufferCount);
     bufferCount = 0;
    }
   }
}

/**
* 刷新输出缓冲区
*/
public void flush() throws IOException{
   if(closed){
    throw new IOException("Cannot flush a closed output stream");
   }
   if(gzipstream != null){
    gzipstream.flush();
   }
}

public void write(int b) throws IOException {
  
   if(closed){
    throw new IOException("Cannot write to a closed output stream");
   }
  
   //如果数据超出了缓冲区(启用压缩的临界值),则启用压缩模式
   if(bufferCount >= buffer.length){
    flushToGZip();
   }
   //如果没有超出缓冲区,则将数据写入缓冲区
   buffer[bufferCount++] = (byte)b;
  
}

public void write(byte[] b,int off,int len) throws IOException{
   if(closed){
    throw new IOException("Cannot write to a closed output stream");
   }
  
   if(len == 0){
    return;
   }
   //如果缓冲区能容纳这些数据则写入缓冲区
   if(len <= buffer.length - bufferCount){
    System.arraycopy(b, off, buffer, bufferCount, len);
    bufferCount += len;
    return;
   }
  
   //如果缓冲区剩余空间不住足,则开启压缩流对象
   flushToGZip();
  
   //如果清空后的缓冲区能够容纳传送过来的数据,则将数据写入缓冲区
   if(len <= buffer.length - bufferCount){
    System.arraycopy(b, off, buffer, bufferCount, len);
    bufferCount += len;
    return;
   }
  
   //如果缓冲区不能容纳传送进来的数据,则直接将数据写入压缩流对象
   writeToGZip(b, off, len);
}


//刷新压缩流对象的缓冲区
public void flushToGZip() throws IOException{
   if(bufferCount > 0){
    writeToGZip(buffer, 0, bufferCount);
    bufferCount = 0;
   }
}

public void writeToGZip(byte b[],int off,int len)throws IOException{
  
   //如果压缩流对象为空,这里初始化它
   if(gzipstream == null){
    response.addHeader("Content-Encoding", "gzip");
    gzipstream = new GZIPOutputStream(output);
   }
  
   //将数据听过压缩流对象输出到response的输出流
   gzipstream.write(b,off,len);
}



public boolean closed(){
   return closed;
}

}



package compressionFilters;

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

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class CompressionServletResponseWrapper extends
   HttpServletResponseWrapper {

/**
* 原始的response对象
*/
protected HttpServletResponse origResponse = null;

protected static final String info = "CompressionServletResponseWrapper";

/**
* response对象的输出流
*/
protected ServletOutputStream stream = null;

/**
* response
*/
protected PrintWriter writer = null;

protected int threshold = 0;

protected String contentType = null;

public CompressionServletResponseWrapper(HttpServletResponse response) {
   super(response);
   origResponse = response;
}

/**
* 设置实体类型
*/
public void setContentType(String contentType){
   this.contentType = contentType;
   origResponse.setContentType(contentType);  
}

/**
* 设置启用压缩的临界值
* @param threshold 临界值
*/
public void setCompressionThreshold(int threshold){
   this.threshold = threshold;
}


public ServletOutputStream createOutputStream() throws IOException{
   CompressionResponseStream stream = new CompressionResponseStream(origResponse);
   stream.setBuffer(threshold);
   return stream;
}

//关闭输出对象
public void finishResponse(){
   try {
    if(writer != null){
     writer.close();
    }else{
     if(stream != null){
      stream.close();
     }
    }
   } catch (IOException e) {

   }
}

public void flushBuffer() throws IOException{
   ((CompressionResponseStream)stream).flush();
}

public ServletOutputStream getOutputStream() throws IOException{
  
   //如果字符流打开,则抛出异常
   if(writer != null)
    throw new IllegalStateException("getWriter() has already been called for this response");
  
   if(stream == null) stream = createOutputStream();
   return stream;
}

public PrintWriter getWriter() throws IOException{
   if(writer != null){
    return (writer);
   }
   if(stream != null)
    throw new IllegalStateException("getOutputStream() has already been called for this response");
  
   stream = createOutputStream();
   String charEnc = origResponse.getCharacterEncoding();
   if(charEnc != null){
    writer = new PrintWriter(new OutputStreamWriter(stream,charEnc));
   }else{
    writer = new PrintWriter(stream);
   }
  
   return writer;
}


private static String getCharsetFromContentType(String type){
   if(type == null){
    return null;
   }
  
   int semi = type.indexOf(":");
   if(semi == -1){
    return null;
   }
  
   String afterSemi = type.substring(semi + 1);
   int charsetLocation = afterSemi.indexOf("charset=");
   if(charsetLocation == -1){
    return null;
   }else{
    String afterCharset = afterSemi.substring(charsetLocation + 8);
    String encoding = afterCharset.trim();
    return encoding;
   }
}
}



reference:
1.http://onjava.com/pub/a/onjava/2003/11/19/filters.html
2.java filter 过滤机制详解 http://hi.baidu.com/linfengtingyu1/blog/item/e14a1af20de0505b352accda.html
3.java.servlet.Filter的应用http://yuhaining.spaces.live.com/Blog/cns!5BBD70DF0F6F839C!307.entry
4.使用filter机制来GZIP压缩网页http://www.javamilk.cn/article/notes/624.htm
  • 大小: 17.5 KB
分享到:
评论
1 楼 wangxiangliao 2013-10-09  
private static boolean isGZipEncoding(HttpServletRequest request){  
39.    boolean flag=false;  
40.    String encoding=request.getHeader("Accept-Encoding");  
41.    if(encoding.indexOf("gzip")!=-1){  
42.      flag=true;  
43.    }  
44.    return flag;  
45.  }  



上面的encoding 没有判断是否为Null ,会抛异常。
最近项目的百度统计代码无法验证,弄了好久,百度的HTTP请求中Accept-Encoding没有设置,获取为null 作死的无法验证,到处找原因。才发现代码中的前任从这里copy的代码。。。。

相关推荐

Global site tag (gtag.js) - Google Analytics