实现GZIP压缩过滤器
这是书上的例子,为了更好的理解,所以自己敲出来实现一下。
以下为我写的代码,对比书上略有些改动。
GzipResponseStream 类:自定义ServletOutputStream类,使其具有缓存、压缩功能。
应用JDK自带的ByteArrayOutputStream类完成缓存response中的数据。
应用JDK自带的GZIPOutputStream类完成压缩。
public class GzipResponseStream extends ServletOutputStream {
//用于捕捉内存缓存中的数据,我对它的理解就是可以自动捕捉输出流的数据,并缓存。
protected ByteArrayOutputStream byteArrayOutputStream = null;
//JDK中自带的GZIP压缩类
protected GZIPOutputStream gzipOutputStream = null;
//接收响应
protected HttpServletResponse response = null;
//
protected ServletOutputStream outputStream = null;
public GzipResponseStream(HttpServletResponse response) throws IOException {
super();
this.response = response;
this.outputStream = response.getOutputStream();
byteArrayOutputStream = new ByteArrayOutputStream();
gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);//将缓存数据放入GZIP流中
System.out.println("init GzipResponseStream");
}
//执行压缩,并将数据输出到浏览器
public void close() throws IOException {
/* if (closed) {
//初始化后,closed为false不执行if语句抛出异常
throw new IOException("This output stream has already been closed");
}*/
//执行压缩,必须要调用这个方法//api:完成写入压缩数据到输出流不关闭底层流。
gzipOutputStream.finish();
//用byte[]接收缓存的数据
byte[] bytes = byteArrayOutputStream.toByteArray();
//设置压缩算法为GZIP,浏览器会自动解压数据
response.addHeader("Content-Length",Integer.toString(bytes.length));
response.addHeader("Content-Encoding","gzip");
//将压缩后的数据输出到浏览器中
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
// closed = true;
}
public void flush() throws IOException {
if (closed) {
throw new IOException("无法刷新关闭的流");
}
gzipOutputStream.flush();
System.out.println("GzipResponseStream flush");
}
//重写write方法,如果流关闭,则抛出异常
@Override
public void write(int b) throws IOException {
/*if (closed) {
throw new IOException("输出流关闭中");
}*/
gzipOutputStream.write((byte)b);
}
//重写write方法
@Override
public void write(byte[] bytes) throws IOException{
/*if (closed) {
throw new IOException("输出流关闭中");
}*/
write(bytes);
}
//重写write方法
@Override
public void write(byte[] bytes, int off, int len) throws IOException{
if (closed) {
throw new IOException("输出流关闭中");
}
gzipOutputStream.write(bytes,off,len);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
public boolean closed() {
return this.closed;
}
}
GZIPResponseWrapper类:只进行对输出内容压缩,不进行将内容输出到客户端的操作。因为response不单单是字符内容,还要处理压缩的内容,即二进制内容,所以它需要重写getOutputStream()和getWriter()方法。
public class GZIPResponseWrapper extends HttpServletResponseWrapper {
//原始的response
private HttpServletResponse response = null;
//自定义的outputStream,对数据进行压缩,并且输出
private GzipResponseStream gzipResponseStream = null;
//自定义的PrintWriter,将内容输出到ServletResponseStream
private PrintWriter printWriter = null;
public GZIPResponseWrapper(HttpServletResponse response) {
super(response);
this.response = response;
}
//覆盖getOutputStream方法,处理二进制内容
@Override
public GzipResponseStream getOutputStream() throws IOException {
if (printWriter != null) {
throw new IllegalStateException("getWriter() har already been called");
}
if (gzipResponseStream != null) {
gzipResponseStream = new GzipResponseStream(response);
System.out.println("GZIPResponseWrapper getOutputStream");
}
return gzipResponseStream;
}
//处理字符内容
@Override
public PrintWriter getWriter() throws IOException {
if (printWriter == null) {
//通过servletOutputStream获得printWriter方法
if (gzipResponseStream == null) {
gzipResponseStream = new GzipResponseStream(response);;
}
printWriter = new PrintWriter(new OutputStreamWriter(gzipResponseStream, "UTF-8"));
}
return (printWriter);
}
//执行这个方法对数据进行GZIP压缩,并输出到浏览器
public void finishResponse() {
try {
if (printWriter != null)
printWriter.close();
if (gzipResponseStream != null)
gzipResponseStream.close();
System.out.println("GzipResponseStream finishResponse");
} catch (IOException e) {
e.printStackTrace();
}
}
//刷新ServletOutputStream输出流
public void flushBuffer() throws IOException {
gzipResponseStream.flush();
}
}
GZIPFilter类:通过检查Accept-Encoding标头是否包含gzip字符来判断浏览器是否支持gzip压缩算法,如果支持则进行GZIP压缩,否则直接输出。
@WebFilter(
description = "内容过滤器",
filterName = "GZIPFilter",
urlPatterns = "/*")
public class GZIPFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
if (req instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
//依据浏览器的Header信息,判断支持的编码方式
String ae = request.getHeader("Accept-Encoding");
if (ae != null && ae.toLowerCase().indexOf("gzip") != -1) {
GZIPResponseWrapper gzipResponseWrapper = new GZIPResponseWrapper(response);
chain.doFilter(req, gzipResponseWrapper );
//输出压缩数据
gzipResponseWrapper.finishResponse();
}else{
chain.doFilter(req,resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
System.out.println("GZIPFilter init!");
}
}
以上就是GZIP压缩过滤器的全部代码,改写了一部分,但是不影响其功能。
新增知识:
1.ByteArrayOutputStream取得输出流中的数据。
2.GZIPOutputStream用于压缩数据。finish()完成将压缩数据写入输出流的操作,无需关闭底层流。