http://www.cnblogs.com/jbelial/archive/2013/07/21/3204415.html
Java EE 下利用Servlet filter接口 和 GZIPOutputStream 包装流 解决web应用中网络传输数据量大的问题
一、Tomcat 直接开启GZIP
gzip是http协议中使用的一种加密算法,客户端向web服务器端发出了请求后,通常情况下服务器端会将页面文件和其他资源,返回到客户端,客户端加载后渲染呈现,这种情况文件一般都比较大,如果开启Gzip ,那么服务器端响应后,会将页面,JS,CSS等文本文件或者其他文件通过高压缩算法将其压缩,然后传输到客户端,由客户端的浏览器负责解压缩与呈现。通常能节省40%以上的流量(一般都有60%左右),一些PHP,JSP文件也能够进行压缩。
打开Tomcat 目录下的conf下的server.xml,并找到如下信息:
Xml代码 收藏代码
<!-- Note : To use gzip compression you could set the following properties :
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml"
-->
把它们加入到你配置的<Connector port="80" .../>中去。如果要压缩css 和 js,加入compressableMimeType="text/html,text/xml,text/css,text/javascript"。还要压缩图片,加入compressableMimeType="text/html,text/xml,text/css,text/javascript,image/gif,image/jpg"。
开启后重启Tomcat ,通过浏览器查看headers信息就能看到是否开启。
二、利用Serlvet 下的filter接口实现GZIP
1、进行gzip压缩条件:
a、 请求头:Accept-Encoding : gzip 告诉服务器,该浏览器支持gzip压缩。b、响应头:Content-Encoding : gzip. 告诉浏览器,输出信息用gzip进行压缩了。
c、两个主要类:
ByteArrayOutputStream : 内存输出流,还有缓存。
GZIPOutputStream 包装流;
2 、gzip 压缩步骤:
a、获取字符的字节数组 byte[] buf = str.getBytes() ;
b、通过GZIPOutputStream 包装流进行输入:
创建 GZIPOutputStream 输出流时,需要传一个带有缓冲区的输出流,所以我们ByteArrayOutputStream 输出流。而且,ByteArrayOutputStream还可以获取byte[];
c、将ByteArrayOutputStream 流中的缓存数据,转换成字节数组。
d、将 压缩后的字节数组通过response 进行输出。不过输出之前要设置Content-Encoding 响应头,value为gzip。告诉浏览器数据进行了gzip压缩,要使用gzip解压。
String str = "我是个测试";
//1\获取字节数组
byte[] bytes = str.getBytes() ;
System.out.println("压缩前的长度:" + bytes.length);
//2\
ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
GZIPOutputStream gzip = new GZIPOutputStream(baos) ;
gzip.write(bytes) ;
gzip.close() ;
//3\
bytes = baos.toByteArray() ;
System.out.println("压缩后的长度:" + bytes.length);
复制代码
数据较小是,压缩的效果不是很明显,不过数据越大,压缩效果越明显。所以,GZIP压缩一般只处理文本内容,对图片、已经压缩过的文件则不进行压缩。这时就要在配置文件时,配置要过滤的资源。
3、GZIPFilter类代码如下
import itheima.decorator.MyHttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
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;
/**
* Gzip压缩过滤器
* @author 贺佐安
*
*/
public class GZIPFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req , ServletResponse resp ,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) resp ;
HttpServletRequest request = (HttpServletRequest) req ;
//创建HttpServletResponse 包装类的实例
MyHttpServletResponse myResponse = new MyHttpServletResponse(response) ;
chain.doFilter(request, myResponse) ;
//GZIP压缩:
byte[] buff = myResponse.getBufferedBytes() ;
//创建缓存容器:
ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
GZIPOutputStream gzip = new GZIPOutputStream(baos) ;
gzip.write(buff) ;
gzip.close() ;
buff = baos.toByteArray() ;
//设置响应头;
response.setHeader("Content-Encoding", "gzip");
response.setContentLength(buff.length) ;
response.getOutputStream().write( buff) ;
}
public void destroy() {
}
}
步骤:
a、对HttpServletResponse 进行包装 :改写getOutputStream()、getWriter() 方法,并且设置一个临时容器,存储Serlvet处理后要输出的数据。 这里是重点。
import java.io.ByteArrayOutputStream;
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;
/**
* 对HttpServletResponse 进行包装
* @author 贺佐安
*
*/
public class MyHttpServletResponse extends HttpServletResponseWrapper {
//定义一个容器,用来存储Serlvet 处理完后response 写出的数据
private ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
private PrintWriter printWriter = null;
public MyHttpServletResponse(HttpServletResponse response) {
super(response) ;
}
//处理字节流输出的情况
public ServletOutputStream getOutputStream() throws IOException {
return new MyServletOutputStream(bos);
}
//处理字符流输出的情况:用字符流时要注意乱码:字节转字符要查码表,字符转字节也要查码表
public PrintWriter getWriter() throws IOException {
printWriter = new PrintWriter(new OutputStreamWriter(bos, super.getCharacterEncoding())) ;
return printWriter;
}
//获取response 写出的数据
public byte[] getBufferedBytes(){
try {
if (printWriter != null)
printWriter.close() ;
bos.flush() ;
} catch (IOException e) {
e.printStackTrace();
}
byte[] byteArray = bos.toByteArray() ;
return byteArray;
}
}
b、改写getOutputStream 方法时,要返回一个SerlvetOutputStream 类实例,因为SerlvetOutputStream是抽象类,不能创建实例,所以要重写SerlvetOutputStream 类:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
/**
* 包装ServletOutputStream ,改写write 方法。
* @author 贺佐安
*
*/
public class MyServletOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null ;
public MyServletOutputStream (ByteArrayOutputStream bos) {
this.bos = bos ;
}
public void write(int b) throws IOException {
bos.write(b) ;
}
}
c、将包装过的HttpServletResponse 类的实例放行。
d、然后获取Servlet 处理过后的数据,然后进行Gzip压缩。
e、调用ServletResponse 的实例,将压缩后的数据写出去。
//GZIP压缩:
byte[] buff = myResponse.getBufferedBytes() ;
//创建缓存容器:
ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
GZIPOutputStream gzip = new GZIPOutputStream(baos) ;
gzip.write(buff) ;
gzip.close() ;
buff = baos.toByteArray() ;
//设置响应头;
response.setHeader("Content-Encoding", "gzip");
response.setContentLength(buff.length) ;
response.getOutputStream().write( buff) ;
以上便是用Filter 对一些文本资源进行GIZP压缩的步骤。重点就是第二步,如何获取Servlet 返回的数据。更细一点的流程如下图: