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