过滤器实现文件的gzip压缩

本文讲述了如何利用j2ee的Filter来实现gzip对网页文件的压缩。配置过滤器在web.xml的第一个位置,拦截*.js文件,确保所有数据在写入response响应流前经过gzip压缩。

gzip和介绍和回想方式,可以参考我的这篇博客 gzip的介绍以及web服务器对文件压缩的支持。本文介绍如何借助j2ee的过滤器Filter自己实现gzip对网页文件的压缩。

将该过滤器配置在web.xml中的第一个,实现对*.js文件拦截

package net.aty;

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
{

    @Override
    public void destroy()
    {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException
    {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String ac = httpRequest.getHeader("accept-encoding");
		
		// 检查浏览器是否支持gzip格式
        if (ac != null && ac.contains("gzip"))
        {
            httpResponse.setHeader("Content-Encoding", "gzip");
			
			//对原始的response进行封装
            GZIPResponseWrapper wrappedResponse = new GZIPResponseWrapper(httpResponse);

            chain.doFilter(httpRequest, wrappedResponse);

            // 完成gzip压缩,将压缩后的数据写入web服务器创建的原始输出流中
            // 一定要手动关闭wrappedResponse,否则httpResponse中数据是空,服务器不能正确的将数据传给浏览器
            wrappedResponse.finishCompress();
        }
        else
        {
            chain.doFilter(request, httpResponse);
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException
    {

    }

}

package net.aty;

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 GZIPResponseWrapper extends HttpServletResponseWrapper
{
    private HttpServletResponse origResponse = null;

    private GZIPResponseStream stream = null;

    private PrintWriter writer = null;

    public GZIPResponseWrapper(HttpServletResponse response) throws IOException
    {
        super(response);
        this.origResponse = response;
    }

    public ServletOutputStream getOutputStream() throws IOException
    {
        if (this.writer != null)
        {
            throw new IllegalStateException("getWriter() has already been called!");
        }

        if (this.stream == null)
        {
            this.stream = this.createOutputStream();
        }
        return this.stream;
    }
	
	public PrintWriter getWriter() throws IOException
    {
        if (this.writer != null)
        {
            return this.writer;
        }

        if (this.stream != null)
        {
            throw new IllegalStateException("getOutputStream() has already been called!");
        }

        this.stream = this.createOutputStream();
        this.writer = new PrintWriter(new OutputStreamWriter(this.stream, "UTF-8"));
        return this.writer;
    }

    public void finishCompress() throws IOException
    {
        if (this.writer != null)
        {
            this.writer.close();
        }
        else if (this.stream != null)
        {
            this.stream.close();
        }
    }

    public long getContentBytes()
    {
        return stream.getWriteBytes();
    }

    private GZIPResponseStream createOutputStream() throws IOException
    {
        return new GZIPResponseStream(origResponse.getOutputStream());
    }

}

GZIPResponseStream 实现对GZIP的封装

package net.aty;

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

import javax.servlet.ServletOutputStream;

// 封装web服务器原始的httpResponse.getOutputStream().服务器会将原本应该写入httpResponse.getOutputStream()
// 中数据,写入到GZIPOutputStream实现数据压缩
public class GZIPResponseStream extends ServletOutputStream
{
    private long writeBytes = 0;

    private GZIPOutputStream gzipStream = null;

    // outputStream是输出目的地。如果在构建gzipStream之前,已经有数据写入outputStream,那么这些数据不会压缩
    public GZIPResponseStream(ServletOutputStream outputStream) throws IOException
    {
        this.gzipStream = new GZIPOutputStream(outputStream);
    }

    @Override
    public void write(int asciiValue) throws IOException
    {
        this.gzipStream.write((byte) asciiValue);
        writeBytes++;
    }

    @Override
    public void write(byte bytesArray[]) throws IOException
    {
        this.write(bytesArray, 0, bytesArray.length);
    }

    @Override
    public void write(byte bytesArray[], int offset, int length) throws IOException
    {
        this.gzipStream.write(bytesArray, offset, length);

        writeBytes += length;
    }

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

    @Override
    public void close() throws IOException
    {
        this.gzipStream.finish();
        this.gzipStream.flush();
        this.gzipStream.close();
    }

    public long getWriteBytes()
    {
        return writeBytes;
    }

}

自己通过调试,得出以下几个结论

1、response对象由web服务器创建,当第一个filter拿到response的时候,内容是空的,这个时候还没有谁向response响应流中写入数据。
当走完最后个filter或servlet的时候,服务器会将静态的html或者动态的jsp写入的reponse的响应流中。

2、GZIPFilter应该放在web.xml中作为第一个过滤器,这样才可以保证所有写入到response中的数据都经过gzip压缩。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值