有两个filter,filter1实现全站gzip压缩,filter2替换关键字,
两个filter单独配置都是可以的,但是两个filter同时运行时,就会报以上错误。
filter1和filter2中都用到了类似PrintWriter out = response.getWriter()这样的代码。
为方便粘贴代码,把filter相关的类写在了一个文件中了。
这个是filter1的代码
package c;import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;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.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;/**
 * Servlet Filter implementation class ContentFilter
 */
@WebFilter(filterName="a",urlPatterns="*")
public class ContentFilter implements Filter {    /**
     * Default constructor. 
     */
    public ContentFilter() {
        // TODO Auto-generated constructor stub
    } /**
 * @see Filter#destroy()
 */
public void destroy() {
// TODO Auto-generated method stub
}
Properties pp=new Properties();
/**
 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
 */
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here // pass the request along the filter chain
HttpServletResponse Response=(HttpServletResponse)response;
MyResponse res=new MyResponse(Response);
chain.doFilter(request, res);
CharArrayWriter myWriter=res.getMyWriter();
String output=myWriter.toString();
for(Object key:pp.keySet())
{
String mykeyString= new String(key.toString().getBytes("ISO8859-1"), "UTF-8");
System.out.println(mykeyString.toString());
output=output.replace(mykeyString.toString(), pp.get(key).toString());
}
PrintWriter out=Response.getWriter();
out.print(output);
out.close();
} /**
 * @see Filter#init(FilterConfig)
 */
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
try {
pp.load(new FileInputStream(new File(fConfig.getServletContext().getRealPath("myword.properties"))));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class MyResponse extends HttpServletResponseWrapper {
private CharArrayWriter myWriter=new CharArrayWriter();
@Override
public PrintWriter getWriter() throws IOException {
// TODO Auto-generated method stub
return new PrintWriter(myWriter);
} public MyResponse(HttpServletResponse response) {
super(response);
// TODO Auto-generated constructor stub
} public CharArrayWriter getMyWriter() {
return myWriter;
} public void setMyWriter(CharArrayWriter myWriter) {
this.myWriter = myWriter;
}
}
这是filter2的代码
package d;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;/**
 * Servlet Filter implementation class gzip
 */
@WebFilter(filterName="gzip",urlPatterns="*.abc")
public class gzip implements Filter {    /**
     * Default constructor. 
     */
    public gzip() {
        // TODO Auto-generated constructor stub
    } /**
 * @see Filter#destroy()
 */
public void destroy() {
// TODO Auto-generated method stub
} /**
 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
 */
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here HttpServletRequest req=(HttpServletRequest)request;
String coding=req.getHeader("Accept-Encoding");
if(coding!=null && coding.indexOf("gzip")!=-1)
{
HttpServletResponse Response=(HttpServletResponse)response;

//用自定义的response替换系统的response
GzipResponseWrapper res=new GzipResponseWrapper(Response);
chain.doFilter(request, res);

//获取截获到的数据
ByteArrayOutputStream inStream=res.getStream();
byte[] data=inStream.toByteArray();
System.out.println("压缩前文件长度:" + data.length);

//开始压缩
ByteArrayOutputStream outStream=new ByteArrayOutputStream();
GZIPOutputStream zip=new GZIPOutputStream(outStream);
zip.write(data);
zip.close();

byte[] gzipdata=outStream.toByteArray();
System.out.println("压缩后文件长度:" + gzipdata.length);
Response.setHeader("content-encoding", "gzip");  
response.setContentLength(gzipdata.length); 
response.getOutputStream().write(gzipdata);
System.out.println(request.getServletContext().getRealPath(""));
}
else
{
// pass the request along the filter chain
chain.doFilter(request, response);
}
} /**
 * @see Filter#init(FilterConfig)
 */
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
class GzipResponseWrapper extends HttpServletResponseWrapper {
ByteArrayOutputStream bout=new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(bout));
public GzipResponseWrapper(HttpServletResponse response) {
super(response);
// TODO Auto-generated constructor stub
} //需要有个函数,可以通过byteArrayOutputStream返回ServletOutputStream

@Override
public ServletOutputStream getOutputStream() throws IOException {
// TODO Auto-generated method stub
return new ServletOutputStream() {

@Override
public void write(int b) throws IOException {
// TODO Auto-generated method stub
bout.write(b);
}
};
}

@Override
public PrintWriter getWriter() throws IOException {
// TODO Auto-generated method stub
return pw;
} public ByteArrayOutputStream getStream() throws IOException {
// TODO Auto-generated method stub
if(pw!=null){  
            pw.close();  
        }  
return bout;
}
}
}

解决方案 »

  1.   

    去掉这句试试。chain.doFilter(request, res);
      

  2.   

    我记得以前我好像也犯过这样的错误。
    原因是如果你前台用的是jsp的话,jsp好像就已经获取了一次输出流了。。
    解决方式好像是jsp获取的那个输出流是一个接口型的,只要获取该继承该接口的输出流的一个输出流就可以了。
    或者别人说的方法
    加上
    out.clear();  
    out = pageContext.pushBody();
    不过我没试过。。
      

  3.   

    filter1用完之后,out.reset()一下
      

  4.   

    这是Java.web整合开发王者归来 这本书上的两个案例,它是单独讲的,我把两个filter放在一起,看看是否两个filter能够同时生效,就出现了这个问题。上面几个办法都不可以。
    1.chain.doFilter(request, res); 这句话去掉了,就不能够把自己定义的response对象传入了,肯定不行的。
    2.out.clear();  
    out = pageContext.pushBody();
    这个在jsp上出现此问题的时候试过,确实可以,filter中,通过getWriter()获得的out对象没有clear方法。
    3.out.reset(),filter中这个方法out也不支持。
    更纠结的是:书上讲对网页压缩的时候,自定义的resonse对象要实现两个方法,getWriter和getOutputStream,前者是输出文字,后者输出二进制,如图片等。我测试了一个有图片的网页,发现只有getWriter会被调用,getOutputStream不会被调用。
      

  5.   

    getWriter和getOutputStream不能被同时调用,一个流程中,只能调用其中的一个,我所说的流程是指当服务器接收到请求之后到请求处理完成这段过程,无论是有多个filter还是多个servlet,他们调用response的进行响应时,通常使用getWriter()或getOutputStream()获得输出的流,这个流指向的是一块缓冲区,这个缓冲区可以自己设置大小,当写入到缓冲区的数据大于缓冲区的size或者响应处理完成进行提交时,数据才会发送到客户端。
    getOutputStream() has already been called for this response  这个错误的原因就是两个方法都被调用了,而且getOutputStream()是先被调用的,在调用getWriter()方法时就报错了。你在filter1和filter2中同时调用了getWriter和getOutputStream,其实他们调用的是同一个过程中response的方法,所以报错了。
    你也可以做个试验,比如servlet1 和 servlet2 ,在servlet1调用getWriter,然后forward到servlet2,在servlet2中调用getOutputStream,这个时候就会报同样的错了。