Filter 全站GZIP压缩过滤的原理及其实现
来源:互联网 发布:怎么制作淘宝客小程序 编辑:程序博客网 时间:2024/06/04 19:02
网上找的两篇不错的response过滤压缩过程与原理
在客户端访问数据时候,为了尽可能高效率的传输,在传输的JSP网页的时候,可以采用GZIP压缩的方式,使得网页经过压缩后再去传输。在此,使用过滤器,对发送到的客户端的显示,都先进行一次压缩。然后再显示,具体流程可以参考下图:
也就是说,当每获得一次请求是的时候,通过对getOutputStream的重写,不让其输出到客户端,而是 将其写入到内存字节数组中去。 然后,当需要输出的时候, 也就是过滤器的第二次执行从chain.doFilter(request,response)开始
再次充内存中取出缓存的数据,进行压缩,并用response进行输出。
package
cn.Filter;
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.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
javax.servlet.http.HttpServletResponseWrapper;
public
class
FilterGZIP
implements
Filter {
public
void
destroy() {
// TODO Auto-generated method stub
}
public
void
doFilter(ServletRequest req, ServletResponse res,
FilterChain chain)
throws
IOException, ServletException {
HttpServletRequest request=(HttpServletRequest) req;
HttpServletResponse response=(HttpServletResponse) res;
//重新封装response ,来达到改写其已知方法的过程
MyResponse mresponse=
new
MyResponse(response);
//以上为过滤器的请求时候的过滤过程;
//当请求的时候,传入的response 的getOutPutStream方法已经被修改过了。
//因此,此时如果页面上调用getOutPutStream 方法的时候,其实调用的是自己写的。
chain.doFilter(request, mresponse);
//请求接受, 开始响应,二次输出的时候,就是开始要将内存中的字节数组,经过压缩传输到客户端的过程了。
ByteArrayOutputStream baos =
new
ByteArrayOutputStream();
GZIPOutputStream gos=
new
GZIPOutputStream(baos);
//从内存中获取到 原始为压缩的数据。
byte
b[]= mresponse.getByte();
System.out.println(
"压缩前共有"
+b.length+
"字节"
);
gos.write(b);
gos.flush();
gos.close();
b=baos.toByteArray();
System.out.println(
"压缩后共有"
+b.length+
"字节"
);
//设置客户端识别打开的方式。
response.setHeader(
"Content-Encoding"
,
"GZIP"
);
response.setContentLength(b.length);
response.getOutputStream().write(b);
}
public
void
init(FilterConfig filterConfig)
throws
ServletException {
// TODO Auto-generated method stub
}
}
以上就是Filter中的全部代码,然后 就是怎样重新封装response的问题了。J2EE中提供了,响应的包装类ServletResponseWrapper 只要继承这个类,重写相应的getOutPutStreaM方法,则目标即刻达成!
class
MyResponse
extends
HttpServletResponseWrapper {
//这个是用来存放页面要输出信息的字节数组。
private
ByteArrayOutputStream baos=
new
ByteArrayOutputStream();
//这个封装的字符流输出对象
private
PrintWriter pw;
public
MyResponse(HttpServletResponse response) {
super
(response);
}
//这个方法,很重要,就是用来获取页面信息存放在内存中的字节数组。 也是压缩的目的
public
byte
[] getByte() {
try
{
if
(pw!=
null
){
pw.flush();
pw.close();
}
baos.flush();
}
catch
(IOException e) {
e.printStackTrace();
}
return
baos.toByteArray();
}
//这个方法,就是我们要重写的方法,但是这个方法,有个返回值ServletOutputStream,而这个类又是个抽象类,所以必须还要把这个类给实现了。可以参考下一个代码块
@Override
public
ServletOutputStream getOutputStream()
throws
IOException {
return
new
MyServletOutputStream(baos);
}
@Override
public
PrintWriter getWriter()
throws
IOException {
pw=
new
PrintWriter(
new
OutputStreamWriter(baos,
super
.getCharacterEncoding()));
return
pw;
}
}
//这个是应上面getOutPutStream 的邀请的返回值。 也是要 servlet中实际调用的方法。 不会输出到页面,只会将servlet中要写的内容放入到内存中去。
class
MyServletOutputStream
extends
ServletOutputStream{
//缓冲页面数据存放区域
private
ByteArrayOutputStream baos;
public
MyServletOutputStream(ByteArrayOutputStream baos){
this
.baos=baos;
}
@Override
public
void
write(
int
b)
throws
IOException {
//将输出写入输出缓存
baos.write(b);
}
}
我自己的理解图形
*****************************************************************************************************************************
GZIP压缩:将压缩后的文本文件,发送给浏览器,减少流量。
一、进行gzip压缩条件:
1、请求头:Accept-Encoding : gzip 告诉服务器,该浏览器支持gzip压缩。
2、响应头:Content-Encoding : gzip. 告诉浏览器,输出信息用gzip进行压缩了。
3、两个主要类:
ByteArrayOutputStream : 内存输出流,还有缓存。
GZIPOutputStream 包装流;
二、gzip 压缩步骤:
1、获取字符的字节数组 byte[] buf = str.getBytes() ;
2、通过GZIPOutputStream 包装流进行输入:
创建 GZIPOutputStream 输出流时,需要传一个带有缓冲区的输出流,所以我们ByteArrayOutputStream 输出流。而且,ByteArrayOutputStream还可以获取byte[];
3、将ByteArrayOutputStream 流中的缓存数据,转换成字节数组。
4、将 压缩后的字节数组通过response 进行输出。不过输出之前要设置Content-Encoding 响应头,value为gzip。告诉浏览器数据进行了gzip压缩,要使用gzip解压。
1 String str = "我是个测试"; 2 //1\获取字节数组 3 byte[] bytes = str.getBytes() ; 4 5 System.out.println("压缩前的长度:" + bytes.length); 6 //2\ 7 ByteArrayOutputStream baos = new ByteArrayOutputStream() ; 8 GZIPOutputStream gzip = new GZIPOutputStream(baos) ; 9 10 gzip.write(bytes) ; 11 gzip.close() ;12 //3\13 bytes = baos.toByteArray() ;14 System.out.println("压缩后的长度:" + bytes.length);
数据较小是,压缩的效果不是很明显,不过数据越大,压缩效果越明显。所以,GZIP压缩一般只处理文本内容,对图片、已经压缩过的文件则不进行压缩。这时就要在配置文件时,配置要过滤的资源。
三、GZIPFilter
1 import itheima.decorator.MyHttpServletResponse; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.IOException; 5 import java.util.zip.GZIPOutputStream; 6 7 import javax.servlet.Filter; 8 import javax.servlet.FilterChain; 9 import javax.servlet.FilterConfig;10 import javax.servlet.ServletException;11 import javax.servlet.ServletRequest;12 import javax.servlet.ServletResponse;13 import javax.servlet.http.HttpServletRequest;14 import javax.servlet.http.HttpServletResponse;15 /**16 * Gzip压缩过滤器17 * @author 贺佐安18 *19 */20 public class GZIPFilter implements Filter{21 22 public void init(FilterConfig filterConfig) throws ServletException {23 }24 25 public void doFilter(ServletRequest req , ServletResponse resp ,26 FilterChain chain) throws IOException, ServletException {27 HttpServletResponse response = (HttpServletResponse) resp ; 28 HttpServletRequest request = (HttpServletRequest) req ;29 //创建HttpServletResponse 包装类的实例30 MyHttpServletResponse myResponse = new MyHttpServletResponse(response) ;31 32 chain.doFilter(request, myResponse) ;33 34 //GZIP压缩:35 byte[] buff = myResponse.getBufferedBytes() ; 36 //创建缓存容器:37 ByteArrayOutputStream baos = new ByteArrayOutputStream() ;38 39 GZIPOutputStream gzip = new GZIPOutputStream(baos) ;40 41 gzip.write(buff) ;42 43 gzip.close() ;44 45 buff = baos.toByteArray() ;46 47 //设置响应头;48 response.setHeader("Content-Encoding", "gzip");49 response.setContentLength(buff.length) ; 50 response.getOutputStream().write( buff) ;51 }52 53 public void destroy() {54 }55 56 }
步骤:
1、对HttpServletResponse 进行包装 :改写getOutputStream()、getWriter() 方法,并且设置一个临时容器,存储Serlvet处理后要输出的数据。 这里是重点。
1 import java.io.ByteArrayOutputStream; 2 import java.io.IOException; 3 import java.io.OutputStreamWriter; 4 import java.io.PrintWriter; 5 6 import javax.servlet.ServletOutputStream; 7 import javax.servlet.http.HttpServletResponse; 8 import javax.servlet.http.HttpServletResponseWrapper; 9 /**10 * 对HttpServletResponse 进行包装11 * @author 贺佐安12 *13 */14 public class MyHttpServletResponse extends HttpServletResponseWrapper {15 //定义一个容器,用来存储Serlvet 处理完后response 写出的数据16 private ByteArrayOutputStream bos = new ByteArrayOutputStream() ;17 private PrintWriter printWriter = null;18 public MyHttpServletResponse(HttpServletResponse response) {19 super(response) ;20 }21 //处理字节流输出的情况22 public ServletOutputStream getOutputStream() throws IOException {23 return new MyServletOutputStream(bos); 24 } 25 26 //处理字符流输出的情况:用字符流时要注意乱码:字节转字符要查码表,字符转字节也要查码表27 public PrintWriter getWriter() throws IOException {28 printWriter = new PrintWriter(new OutputStreamWriter(bos, super.getCharacterEncoding())) ;29 return printWriter;30 }31 //获取response 写出的数据32 public byte[] getBufferedBytes(){33 try {34 if (printWriter != null) 35 printWriter.close() ;36 bos.flush() ;37 } catch (IOException e) {38 e.printStackTrace(); 39 }40 byte[] byteArray = bos.toByteArray() ;41 return byteArray;42 }43 }
2、改写getOutputStream 方法时,要返回一个SerlvetOutputStream 类实例,因为SerlvetOutputStream是抽象类,不能创建实例,所以要重写SerlvetOutputStream 类:
1 import java.io.ByteArrayOutputStream; 2 import java.io.IOException; 3 4 import javax.servlet.ServletOutputStream; 5 /** 6 * 包装ServletOutputStream ,改写write 方法。 7 * @author 贺佐安 8 * 9 */10 public class MyServletOutputStream extends ServletOutputStream {11 private ByteArrayOutputStream bos = null ; 12 public MyServletOutputStream (ByteArrayOutputStream bos) {13 this.bos = bos ;14 }15 public void write(int b) throws IOException {16 bos.write(b) ;17 }18 }
3、将包装过的HttpServletResponse 类的实例放行。
4、然后获取Servlet 处理过后的数据,然后进行Gzip压缩。
5、调用ServletResponse 的实例,将压缩后的数据写出去。
1 //GZIP压缩: 2 byte[] buff = myResponse.getBufferedBytes() ; 3 //创建缓存容器: 4 ByteArrayOutputStream baos = new ByteArrayOutputStream() ; 5 6 GZIPOutputStream gzip = new GZIPOutputStream(baos) ; 7 8 gzip.write(buff) ; 9 10 gzip.close() ;11 12 buff = baos.toByteArray() ;13 14 //设置响应头;15 response.setHeader("Content-Encoding", "gzip");16 response.setContentLength(buff.length) ; 17 response.getOutputStream().write( buff) ;
以上便是用Filter 对一些文本资源进行GIZP压缩的步骤。重点就是第二步,如何获取Servlet 返回的数据。更细一点的流程如下图:
- Filter 全站GZIP压缩过滤的原理及其实现
- 过滤器(Filter)应用:全站压缩----Gzip网页压缩输出
- 使用filter实现网站的gzip压缩
- 全站Gzip压缩原理详解
- java web使用filter进行全站压缩的原理及实现
- 通过Filter和HttpServletResponseWrapper,实现Gzip压缩
- 全站GZIP压缩过滤器
- Filter-全站压缩
- filter实现过滤压缩指定的请求的操作
- Http gzip的压缩原理
- GZIP压缩Filter
- gzip,zip压缩格式和png图像格式的本质压缩算法----DEFLATE及其实现:zlib
- Filter技术实现全网站的压缩输出
- 过滤器Filter的案例4——实现全站的数据的压缩
- javaweb--Filter全站压缩
- 解决全站压缩Filter
- javaEE之------GZIP全站压缩
- 9Filter高级开发2--实现全站压缩
- 如何使用strace+pstack利器分析程序性能
- _IO, _IOR, _IOW, _IOWR 宏的用法与解析
- Zoho CRM:管理者该管什么
- 【QT环境搭建】qt字库的移植(使其很好的显示中文)
- 动态获取UILabel的高度和宽度
- Filter 全站GZIP压缩过滤的原理及其实现
- 干掉腾讯讨厌的AndroidAssist,AndroidServer
- linux下SCP命令的使用
- maven新手之插件初探
- weblogic.Admin
- 初来乍到
- AOP的两种实现方式
- NSData NSURL 字符串网络请求的时候 互相转换
- c语言全局变量const static int