package com.itheima.filter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class GzipFilter implements Filter {
private FilterConfig config;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String encoding = config.getInitParameter("encoding");
request.setCharacterEncoding(encoding);
response.setContentType("text/html;charset="+encoding);
//
//response.setCharacterEncoding("")
// HttpServletRequestWrapper ---对request 进行 包装的包装类 ---- 解决全站的 请求数据的乱码
//HttpServletResponseWrapper ----对response 进行 包装的 包装类 ---- 解决全站的 响应的数据的 压缩
MyHttpServletResponseWrapper myresponse = new MyHttpServletResponseWrapper(response);
//放行
chain.doFilter(request, myresponse); // --- 目标 资源会得到 执行
System.out.println("之后 ...");
// 将 写到 myresponse 里的数据 进行 压缩 , 然后 输出到 浏览器 去
//这里 报错的 原因是 因为
// 没有 原始的数据(压缩前的数据), 那么 如果 能够 将原始的 压缩前的数据 拿到
// 所有的问题就 都解决了...
//byte[] b = data.getBytes();
byte[] b = myresponse.getOldData();
System.out.println("压缩前: " + b.length);
//底层流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//采用gzip压缩
//这里 压缩的时候, 需要的是一个 输出流, 这里的输出流是一个底层流
GZIPOutputStream gout= new GZIPOutputStream(baos);
gout.write(b);
gout.close(); //确保 数据可以写到 底层流 baos中去
//这里 要注意: 由于 数据是写到 底层流 baos中去的, gout 默认的是有 缓冲区的 .
b = baos.toByteArray();
System.out.println("压缩后 : " +b.length);
//告诉浏览器 需要 解压缩 数据-- 通过 http的响应头 实现
response.setHeader("content-encoding", "gzip");
response.setContentLength(b.length); //数据的长度
response.getOutputStream().write(b);
}
@Override
public void destroy() {
}
}
class MyHttpServletResponseWrapper extends HttpServletResponseWrapper{
private HttpServletResponse response;
public MyHttpServletResponseWrapper(HttpServletResponse response) {
super(response);
this.response= response;
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
//通过 这个方法 可以 拿到 原始的 数据
public byte[] getOldData(){
if(pw!=null){
pw.close();
}
return bout.toByteArray();
}
PrintWriter pw = null;
@Override
public PrintWriter getWriter() throws IOException {
//字符输出流, 有缓冲区 ,
if(pw==null){
//字符输出流 ----- bout (字节流)
pw = new PrintWriter(new OutputStreamWriter(bout, response.getCharacterEncoding()));
}
return pw;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
//在 调用这个 方法的时候, 数据 写到 一个 俺们自己 定义的输出流去, 那么这样 数据就进入了 自己 定义的输出流中去 了 .
return new MyServletOutputStream(bout);
}
}
class MyServletOutputStream extends ServletOutputStream{
private OutputStream out;
public MyServletOutputStream(OutputStream out){
this.out = out;
}
//最终 是 调用到 这个 方法里 了 .
//所有的数据 都进入 到了 out 中去 了,而这里 out 又 是 MyHttpServletResponseWrapper 里的 bout 了, 所以 所有的数据 都 在 bout 中了
@Override
public void write(int b) throws IOException {
out.write(b);
}
// 这里 没有 一个 接受 字节 数组的write 方法
}