servlet 实现过滤器中向浏览器发送压缩数据流

参考:http://fengyanzhang.iteye.com/admin/blogs/1853733

在如上博文的过滤器中,向浏览器发送压缩数据流失败,先给出正确的数据压缩功能的过滤器:

web.xml:

    <filter>
<filter-name>CompressionFilter</filter-name>
<filter-class>com.fyz.filter.CompressionFilter</filter-class>
<init-param>
    <param-name>compressionThreshold</param-name>
    <param-value>100</param-value>
</init-param>
<init-param>
    <param-name>debug</param-name>
    <param-value>10</param-value>
</init-param>
    </filter>
    <filter-mapping>
<filter-name>CompressionFilter</filter-name>
<url-pattern>/*</url-pattern>
    </filter-mapping>



具体的过滤器:


package com.fyz.filter;

import java.io.IOException;
import java.util.Enumeration;
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 CompressionFilter implements Filter {

    private FilterConfig config = null;
    private int minThreshold = 128;
    protected int compressionThreshold;  //压缩极限
    private int debug = 0;

    public void init(FilterConfig filterConfig) {  //初始化代码,得到初始化的值

config = filterConfig;
if (filterConfig != null) {
    String value = filterConfig.getInitParameter("debug");

    if (value != null) {
debug = Integer.parseInt(value);
    } else {
debug = 0;
    }

    String str = filterConfig.getInitParameter("compressionThreshold");
    if (str != null) {
compressionThreshold = Integer.parseInt(str);
if (compressionThreshold != 0 && compressionThreshold < minThreshold) {
    if (debug > 0) {
System.out.println("compressionThreshold should be either 0 - no compression or >= " + minThreshold);
System.out.println("compressionThreshold set to " + minThreshold);
    }
    compressionThreshold = minThreshold;
}
    } else {
compressionThreshold = 0;
    }

} else {
    compressionThreshold = 0;
}

    }

    public void destroy() {

this.config = null;

    }

    public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
if (debug > 0) {
    System.out.println("@doFilter");
}

if (compressionThreshold == 0) {
    if (debug > 0) {
System.out.println("doFilter gets called, but compressionTreshold is set to 0 - no compression");
    }
    chain.doFilter(request, response);
    return;
}

boolean supportCompression = false;
if (request instanceof HttpServletRequest) {
    if (debug > 1) {
System.out.println("requestURI = " + ((HttpServletRequest) request).getRequestURI());
    }

    // Are we allowed to compress ?
    String s = (String) ((HttpServletRequest) request).getParameter("gzip");
    if ("false".equals(s)) {
if (debug > 0) {
    System.out.println("got parameter gzip=false --> don't compress, just chain filter");
}
chain.doFilter(request, response);
return;
    }

    Enumeration e =
    ((HttpServletRequest) request).getHeaders("Accept-Encoding");
    while (e.hasMoreElements()) {
String name = (String) e.nextElement();
if (name.indexOf("gzip") != -1) {
    if (debug > 0) {
System.out.println("supports compression");
    }
    supportCompression = true;
} else {
    if (debug > 0) {
System.out.println("no support for compresion");
    }
}
    }
}

if (!supportCompression) {
    if (debug > 0) {
System.out.println("doFilter gets called wo compression");
    }
    chain.doFilter(request, response);
    return;
} else {
    if (response instanceof HttpServletResponse) {
CompressionServletResponseWrapper wrappedResponse =
new CompressionServletResponseWrapper((HttpServletResponse) response);
wrappedResponse.setDebugLevel(debug);
wrappedResponse.setCompressionThreshold(compressionThreshold);
if (debug > 0) {
    System.out.println("doFilter gets called with compression");
}
try {
    chain.doFilter(request, wrappedResponse);
} finally {
    wrappedResponse.finishResponse();
}
return;
    }
}
    }

    public void setFilterConfig(FilterConfig filterConfig) {
init(filterConfig);
    }

    public FilterConfig getFilterConfig() {
return config;
    }
}

重构response:


package compressionFilters;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
* Implementation of <b>HttpServletResponseWrapper</b> that works with
* the CompressionServletResponseStream implementation..
*
* @author Amy Roh
* @author Dmitri Valdin
* @version $Revision$, $Date$
*/

public class CompressionServletResponseWrapper extends HttpServletResponseWrapper {
   
    protected HttpServletResponse origResponse = null;
   
    public CompressionServletResponseWrapper(HttpServletResponse response) {
        super(response);
        origResponse = response;
        if (debug > 1) {
            System.out.println("CompressionServletResponseWrapper constructor gets called");
        }
    }

    protected static final String info = "CompressionServletResponseWrapper";

    /**
     * The ServletOutputStream that has been returned by
     * <code>getOutputStream()</code>, if any.
     */

    protected ServletOutputStream stream = null;


    /**
     * The PrintWriter that has been returned by
     * <code>getWriter()</code>, if any.
     */

    protected PrintWriter writer = null;

    /**
     * The threshold number to compress
     */
    protected int threshold = 0;

    /**
     * Debug level
     */
    private int debug = 0;

    /**
     * Content type
     */
    protected String contentType = null;

    // --------------------------------------------------------- Public Methods


    /**
     * Set content type
     */
    public void setContentType(String contentType) {
        if (debug > 1) {
            System.out.println("setContentType to "+contentType);
        }
        this.contentType = contentType;
        origResponse.setContentType(contentType);
    }


    /**
     * Set threshold number
     */
    public void setCompressionThreshold(int threshold) {
        if (debug > 1) {
            System.out.println("setCompressionThreshold to " + threshold);
        }
        this.threshold = threshold;
    }


    /**
     * Set debug level
     */
    public void setDebugLevel(int debug) {
        this.debug = debug;
    }


    /**
     * Create and return a ServletOutputStream to write the content
     * associated with this Response.
     *
     * @exception IOException if an input/output error occurs
     */
    public ServletOutputStream createOutputStream() throws IOException {
        if (debug > 1) {
            System.out.println("createOutputStream gets called");
        }

        CompressionResponseStream stream = new CompressionResponseStream(origResponse);
        stream.setDebugLevel(debug);
        stream.setBuffer(threshold);

        return stream;

    }


    /**
     * Finish a response.
     */
    public void finishResponse() {
        try {
            if (writer != null) {
                writer.close();
            } else {
                if (stream != null)
                    stream.close();
            }
        } catch (IOException e) {
        }
    }


    // ------------------------------------------------ ServletResponse Methods


    /**
     * Flush the buffer and commit this response.
     *
     * @exception IOException if an input/output error occurs
     */
    public void flushBuffer() throws IOException {
        if (debug > 1) {
            System.out.println("flush buffer @ CompressionServletResponseWrapper");
        }
        ((CompressionResponseStream)stream).flush();

    }

    /**
     * Return the servlet output stream associated with this Response.
     *
     * @exception IllegalStateException if <code>getWriter</code> has
     *  already been called for this response
     * @exception IOException if an input/output error occurs
     */
    public ServletOutputStream getOutputStream() throws IOException {

        if (writer != null)
            throw new IllegalStateException("getWriter() has already been called for this response");

        if (stream == null)
            stream = createOutputStream();
        if (debug > 1) {
            System.out.println("stream is set to "+stream+" in getOutputStream");
        }

        return (stream);

    }

    /**
     * Return the writer associated with this Response.
     *
     * @exception IllegalStateException if <code>getOutputStream</code> has
     *  already been called for this response
     * @exception IOException if an input/output error occurs
     */
    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();
        if (debug > 1) {
            System.out.println("stream is set to "+stream+" in getWriter");
        }
        //String charset = getCharsetFromContentType(contentType);
        String charEnc = origResponse.getCharacterEncoding();
        if (debug > 1) {
            System.out.println("character encoding is " + charEnc);
        }
        // HttpServletResponse.getCharacterEncoding() shouldn't return null
        // according the spec, so feel free to remove that "if"
        if (charEnc != null) {
            writer = new PrintWriter(new OutputStreamWriter(stream, charEnc));
        } else {
            writer = new PrintWriter(stream);
        }
       
        return (writer);

    }


    public void setContentLength(int length) {
    }


    /**
     * Returns character from content type. This method was taken from tomcat.
     * @author rajo
     */
    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 + ;
            String encoding = afterCharset.trim();
            return encoding;
        }
    }

}



输出流:


package com.fyz.filter;

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

/**
* Implementation of <b>ServletOutputStream</b> that works with the
* CompressionServletResponseWrapper implementation.
*
* @author Amy Roh
* @author Dmitri Valdin
* @version $Revision$, $Date$
*/
public class CompressionResponseStream
extends ServletOutputStream {

    public CompressionResponseStream(HttpServletResponse response) throws IOException {

super();
closed = false;
this.response = response;
this.output = response.getOutputStream();

    }
    protected int compressionThreshold = 0;
    private int debug = 0;
    protected byte[] buffer = null; //输出字节缓冲
    protected int bufferCount = 0;
    protected GZIPOutputStream gzipstream = null;
    /**
     * Has this stream been closed?
     */
    protected boolean closed = false;
    /**
     * The content length past which we will not write, or -1 if there is no
     * defined content length.
     */
    protected int length = -1;
    /**
     * The response with which this servlet output stream is associated.
     */
    protected HttpServletResponse response = null;
    /**
     * The underlying servket output stream to which we should write data.
     */
    protected ServletOutputStream output = null;

    // --------------------------------------------------------- Public Methods
    /**
     * Set debug level
     */
    public void setDebugLevel(int debug) {
this.debug = debug;
    }

    /**
     * Set the compressionThreshold number and create buffer for this size
     */
    protected void setBuffer(int threshold) {
compressionThreshold = threshold;
buffer = new byte[compressionThreshold];
if (debug > 1) {
    System.out.println("buffer is set to " + compressionThreshold);
}
    }

    /**
     * Close this output stream, causing any buffered data to be flushed and any
     * further output data to throw an IOException.
     */
    @Override
    public void close() throws IOException {

if (debug > 1) {
    System.out.println("close() @ CompressionResponseStream");
}
if (closed) {
    throw new IOException("This output stream has already been closed");
}

if (gzipstream != null) {
    flushToGZip();
    gzipstream.close();
    gzipstream = null;
} else {
    if (bufferCount > 0) {
if (debug > 2) {
    System.out.print("output.write(");
    System.out.write(buffer, 0, bufferCount);
    System.out.println(")");
}
output.write(buffer, 0, bufferCount);
bufferCount = 0;
    }
}

output.close();
closed = true;

    }

    /**
     * Flush any buffered data for this output stream, which also causes the
     * response to be committed.
     */
    public void flush() throws IOException {

if (debug > 1) {
    System.out.println("flush() @ CompressionResponseStream");
}
if (closed) {
    throw new IOException("Cannot flush a closed output stream");
}

if (gzipstream != null) {
    gzipstream.flush();
}

    }

    public void flushToGZip() throws IOException {

if (debug > 1) {
    System.out.println("flushToGZip() @ CompressionResponseStream");
}
if (bufferCount > 0) {
    if (debug > 1) {
System.out.println("flushing out to GZipStream, bufferCount = " + bufferCount);
    }
    writeToGZip(buffer, 0, bufferCount);
    bufferCount = 0;
}

    }

    /**
     * Write the specified byte to our output stream.
     *
     * @param b The byte to be written
     *
     * @exception IOException if an input/output error occurs
     */
    public void write(int b) throws IOException {

if (debug > 1) {
    System.out.println("write " + b + " in CompressionResponseStream ");
}
if (closed) {
    throw new IOException("Cannot write to a closed output stream");
}

if (bufferCount >= buffer.length) {
    flushToGZip();
}

buffer[bufferCount++] = (byte) b;

    }

    /**
     * Write
     * <code>b.length</code> bytes from the specified byte array to our output
     * stream.
     *
     * @param b The byte array to be written
     *
     * @exception IOException if an input/output error occurs
     */
    public void write(byte b[]) throws IOException {

write(b, 0, b.length);

    }

    /**
     * Write
     * <code>len</code> bytes from the specified byte array, starting at the
     * specified offset, to our output stream.
     *
     * @param b The byte array containing the bytes to be written
     * @param off Zero-relative starting offset of the bytes to be written
     * @param len The number of bytes to be written
     *
     * @exception IOException if an input/output error occurs
     */
    public void write(byte b[], int off, int len) throws IOException {

if (debug > 1) {
    System.out.println("write, bufferCount = " + bufferCount + " len = " + len + " off = " + off);
}
if (debug > 2) {
    System.out.print("write(");
    System.out.write(b, off, len);
    System.out.println(")");
}

if (closed) {
    throw new IOException("Cannot write to a closed output stream");
}

if (len == 0) {
    return;
}

// Can we write into buffer ?
if (len <= (buffer.length - bufferCount)) {
    System.arraycopy(b, off, buffer, bufferCount, len);
    bufferCount += len;
    return;
}

// There is not enough space in buffer. Flush it ...
flushToGZip();

// ... and try again. Note, that bufferCount = 0 here !
if (len <= (buffer.length - bufferCount)) {
    System.arraycopy(b, off, buffer, bufferCount, len);
    bufferCount += len;
    return;
}

// write direct to gzip
writeToGZip(b, off, len);
    }

    public void writeToGZip(byte b[], int off, int len) throws IOException {

if (debug > 1) {
    System.out.println("writeToGZip, len = " + len);
}
if (debug > 2) {
    System.out.print("writeToGZip(");
    System.out.write(b, off, len);
    System.out.println(")");
}
if (gzipstream == null) {
    if (debug > 1) {
System.out.println("new GZIPOutputStream");
    }
    response.addHeader("Content-Encoding", "gzip");
    gzipstream = new GZIPOutputStream(output);
}
gzipstream.write(b, off, len);

    }

    // -------------------------------------------------------- Package Methods
    /**
     * Has this response stream been closed?
     */
    public boolean closed() {

return (this.closed);

    }
}

你可能感兴趣的:(servlet,浏览器)