您的位置:首页 > 移动开发

GzipFilter压缩过滤器(应用:手机端传输数据压缩处理)

2013-03-29 15:34 369 查看
//推荐使用的!  亲测可用
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
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;

//注解+动态代理
public class GzipFilter implements Filter {

public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {

final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) resp;

final ByteArrayOutputStream bout = new ByteArrayOutputStream();
final PrintWriter pw =  new PrintWriter(new OutputStreamWriter(bout,response.getCharacterEncoding()));

//response.getOuputSstream getWriter
//ResponseProxy.get
chain.doFilter(request, (ServletResponse) Proxy.newProxyInstance(GzipFilter.class.getClassLoader(), response.getClass().getInterfaces(), new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

if(method.getName().equals("getOutputStream")){  //MyServletOutputStream.write
return new MyServletOutputStream(bout);   //需要压缩的话先要获得数据,但是先不写出去,先放在bout流.其实就是返回一个bout和重写了write的流
}else if(method.getName().equals("getWriter")){
return pw;
}else{
return method.invoke(response, args);
}
}
}));

if(pw!=null){
pw.close();
}
//此时数据已经封装到了bout中,因为拿到的就是 new MyServletOutputStream(bout);
byte data[] = bout.toByteArray(); //获得写出去的数据,截留下来, 不管调用是哪个流,最终都会被写到bout中去,这时要注意,filter要捕获servlet的输出,并把servlet输出写到buffer,然后,把数据再写给浏览器.
ByteArrayOutputStream bout2 = new ByteArrayOutputStream();  //再定义一个低层流,此类实现了一个输出流,其中的数据被写入一个 byte 数组。
//缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。

GZIPOutputStream gout = new GZIPOutputStream(bout2);   //把获得的数据压缩之后先不写出去,先写到bout2流
gout.write(data);     //将数据写到一个流中
gout.close();
byte gzip[] = bout2.toByteArray();  //获得写完了之后的压缩数据

response.setHeader("content-encoding", "gzip");
response.setContentLength(gzip.length);
response.getOutputStream().write(gzip);  //最后采用response把压缩后的数据写出去

}

class MyServletOutputStream extends ServletOutputStream{

private ByteArrayOutputStream bout;
public MyServletOutputStream(ByteArrayOutputStream bout){
this.bout = bout;
}
@Override
public void write(int b) throws IOException {
this.bout.write(b);
}

}

public void destroy() {
// TODO Auto-generated method stub

}

public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub

}

}


扩展阅读代码:

package cn.icoke.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.zip.GZIPOutputStream;

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.Filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;

/*
*author xiaozhe
*
*
*/
public class GzipFilter implements Filter {

/**
* Constructor of the object.
*/
public GzipFilter() {
super();
}

private FilterConfig filterConfig;

public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}

public void doFilter(ServletRequest req, ServletResponse res,
FilterChain filterChain) {
try {

HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession();

ProxyResponse rpoxy = new ProxyResponse(response);

filterChain.doFilter(request, rpoxy.getResponse());
byte[] result = rpoxy.toByteArray();
System.out.println("压缩前的数据大小:"+ result.length);
//实现压缩数据
byte[] gzipResult = gzip(result);
System.err.println("压缩后的数据大小:"+gzipResult.length);
response.setHeader("Content-Encoding","gzip");
response.setHeader("Content-Length", gzipResult.length+"");
response.getOutputStream().write(gzipResult);

} catch (IOException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}//end method doFilter()

private byte[] gzip(byte[] result) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(out);
gout.write(result);
gout.close();
return out.toByteArray();
}

public void destroy() {

}

}//end class

/*
* response 装饰设计模式实现
*/
/*
class RaiseResponse extends HttpServletResponseWrapper{

private ByteArrayOutputStream out = new ByteArrayOutputStream();
private PrintWriter pw = null;
private HttpServletResponse response =
(HttpServletResponse) super.getResponse();

public RaiseResponse(HttpServletResponse response) {
super(response);
}

@Override
public ServletOutputStream getOutputStream() throws IOException {
return new RaiseOutputStream(out);
}

@Override
public PrintWriter getWriter() throws IOException {
//字符流在再转换成字节流是 注意要用转换流OutputStreamWriter去转换并设置编码集 不然会出现乱码现象
pw = new PrintWriter(new OutputStreamWriter(out,response.getCharacterEncoding()));
return pw;
}

public byte[] toByteArray(){
if(pw != null){
pw.close();
}
return out.toByteArray();
}
}//end class
class RaiseOutputStream extends ServletOutputStream{
ByteArrayOutputStream out = null;
public RaiseOutputStream(ByteArrayOutputStream out){
this.out = out;
}
@Override
public void write(int b) throws IOException {
out.write(b);
}
}//end class
*/

/*
*代理模式 实现数据压缩
*/
class ProxyResponse {

private HttpServletResponse response;
private ByteArrayOutputStream out = new ByteArrayOutputStream();
private PrintWriter pw = null;
public ProxyResponse(HttpServletResponse response){
this.response = response;
}
public HttpServletResponse getResponse(){
return (HttpServletResponse) Proxy.newProxyInstance(ProxyResponse.class.getClassLoader(),
response.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if(method.getName().equals("getOutputStream")){
return new RaiseServletOutputStream(out);
}
if(method.getName().equals("getWriter")){
pw = new PrintWriter(
new OutputStreamWriter(out,response.getCharacterEncoding()));
return pw;
}
return method.invoke(response, args);
}
});
}//end method

public byte[] toByteArray(){
if(pw != null){
pw.close();
}
return out.toByteArray();
}//end method

}//end class
class RaiseServletOutputStream extends ServletOutputStream{

ByteArrayOutputStream out = null;
public RaiseServletOutputStream(ByteArrayOutputStream out ){
this. out = out;
}
@Override
public void write(int b) throws IOException {
out.write(b);
}
}

/*
* 在配置一个 压缩功能的filter时要注意 <dispatcher>FORWARD</dispatcher>
* 拦截方式要大写 可以同时配置多个拦截方式
*
*     <filter>
<filter-name>GzipFilter</filter-name>
<filter-class>cn.icoke.filter.GzipFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
*
*
*/


其他应用案例

案例1:
分析:因为客户端接受的数据只有那么几种,其中GZIP是最长用的一种,所以我们就需要知道怎么在服务器把数据压缩后发给客户端
1:首先服务端要有数据
2:把数据读到内存中
3:把内存中的数据读到流中
具体如下:
public void doGet(HttpServletRequest request, HttpServletResponse respose)
throws ServletException, IOException {
String str="xxxxxxxxxxxxxxxxx";
ByteArrayOutputStream bos=newByteArrayOutputStream();
GZIPOutputStream gos=new GZIPOutputStream(bos);
gos.write(str.getBytes());
gos.close();
byte[] buff=bos.toArrayByte();//读取压缩后的数据
response.setHeader("Content-Encoding","gzip");//通知客户端浏览器的数据压缩格式
response.getOutputStream().write(buff);//把压缩后的数据打印到客户端
}
案例2:
响应消息头(302与setHeader())的请求重定向:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setStatus(302);
response.setHeader("Location", "/day/1.html");
}
案例3:
在一个页面上每隔3秒钟产生一个随机数
代码如下:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Random r=new Random();
response.setHeader("Refresh", "3");//如果想3秒后转到一个页面只需这样既可("Refresh","3;URL="/day/2.html"");
response.getOutputStream().write((r.nextInt()+"").getBytes());
}
案例4:
分析:
1:首先要得到图片的路径,通过ServletContext得到
2:把图片读取到流中
3:通知浏览器下载
4:把数据打印出去
注意:可能这个案例目前有些简单,因为关于图片名字为中文时出现乱码该怎么处理,在这里没有具体说出,不过
在以后会细说。
图片的下载问题
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String realPath=ServletContext().getRealPath("/xxxxx");//得到图片的路径
InputStream in=new FileInputStream(realPath);//把图片读取到流中
response.setHeander("Content-Disposition","attachment;fileName=1.jpg");//通知浏览器下载
byte[] buf=new byte[1024];//定义一个缓冲区
int len=0;
OutputStream os=response.getOutputStream();//得到输出流对象
while((in.read(buf))!=-1){
out.write(buf,0,len);
}
out.close();
in.close();
}
案例5:
缓冲问题
如何不让浏览器缓冲我们服务端的数据,因为动态数据是不需要缓存的
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String str="aaaaaaaa";
response.setHeader("Expires","-1");//通知浏览器不要缓存数据,当第二个参数小于0时,表示不缓存
response.setHeader("Cache-Control","no-cache");//这是针对HTTP1.1
response.setHeader("Pragma","no-cache");//这是针对HTTP1.0
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: