JSP Filter,GZIP压缩响应流

本文介绍了一种使用Java Servlet Filter实现的GZIP压缩方法,通过自定义的GZipStream、GZipResponse及GZipFilter类,实现了对HTTP响应内容的压缩,以此来减少数据传输量并提升用户访问速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

url:[url]http://hi.baidu.com/xhftx/blog/item/fbc11d3012648711ebc4af59.html[/url]
[size=medium]关键词:JSP,Filter,Servlet,GZIP

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

用Filter实现对返回信息的压缩,代码参考Tomcat examples里面的[/size]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>


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

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



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 过滤机制详解 [url]http://hi.baidu.com/linfengtingyu1/blog/item/e14a1af20de0505b352accda.html[/url]
3.java.servlet.Filter的应用[url]http://yuhaining.spaces.live.com/Blog/cns!5BBD70DF0F6F839C!307.entry[/url]
4.使用filter机制来GZIP压缩网页[url]http://www.javamilk.cn/article/notes/624.htm[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值