有两个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;
}
}
}
两个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;
}
}
}
原因是如果你前台用的是jsp的话,jsp好像就已经获取了一次输出流了。。
解决方式好像是jsp获取的那个输出流是一个接口型的,只要获取该继承该接口的输出流的一个输出流就可以了。
或者别人说的方法
加上
out.clear();
out = pageContext.pushBody();
不过我没试过。。
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不会被调用。
getOutputStream() has already been called for this response 这个错误的原因就是两个方法都被调用了,而且getOutputStream()是先被调用的,在调用getWriter()方法时就报错了。你在filter1和filter2中同时调用了getWriter和getOutputStream,其实他们调用的是同一个过程中response的方法,所以报错了。
你也可以做个试验,比如servlet1 和 servlet2 ,在servlet1调用getWriter,然后forward到servlet2,在servlet2中调用getOutputStream,这个时候就会报同样的错了。