Java Web 减少网络 IO、静态资源磁盘 IO 有效的办法--响应使用 GZIP( 压缩http请求与响应gzip压缩)
(转载http://blog.csdn.net/hylclxy/article/details/7779662)
出于节约流量考虑, 客户端在向服务端发送request的时候对post数据进行gzip压缩, 同时服务端把返回的数据也进行gzip压缩. 为防止遗忘, 记录在此.
编写工具类GzipUtil.java, 开始没考虑好, 方法实现得较乱:
public static String METHOD_POST = "POST";
public static final String ACCEPT_ENCODING = "Accept-Encoding";
public static final String CONTENT_ENCOING = "Content-Encoding";
public static final String CONTENT_LENGTH = "Content-Length";
public static final String ENCODING_GZIP = "gzip";
public static final String MIME_APPLICATION_X_GZIP = "application/x-gzip";
public static final String ENCODING = "UTF-8"; /**
* 对参数进行gzip压缩操作, 返回字节数组
* @param data 待压缩的字节数组
* @return 压缩后的gzip字节数组
* @throws IOException
*/
public static byte[] gzip(byte[] data) throws IOException{ ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gos = new GZIPOutputStream(baos); gos.write(data); gos.finish();
gos.flush(); byte[] result = baos.toByteArray(); baos.flush(); try{
gos.close();
}catch(IOException e){
logger.warn("Close GZIPOutputStream fail:", e);
} return result;
} /**
* 对参数进行gzip压缩操作, 结果输出到参数的输出流中
* @param data 待压缩的字节数组
* @param os 压缩后的输出流
* @throws IOException
*/
public static void gzip(byte[] data, OutputStream os) throws IOException{ GZIPOutputStream gos = new GZIPOutputStream(os); gos.write(data); gos.finish();
gos.flush(); try{
gos.close();
}catch(IOException e){
logger.warn("Close GZIPOutputStream fail:", e);
} } /**
* 对输入流进行gzip压缩
* @param ins 待压缩的输入流
* @param os 压缩后的输出流
* @throws IOException
*/
public static void gzip(InputStream ins, OutputStream os) throws IOException{ GZIPOutputStream gos = new GZIPOutputStream(os); int b;
while((b = ins.read()) != -1){
gos.write(b);
} gos.finish();
gos.flush(); } /**
* 解压缩
* @param ins 输入流
* @return 解压完的数据字节数组
* @throws IOException
*/
public static byte[] unGzip(InputStream ins) throws IOException{ if(logger.isInfoEnabled()){
logger.info("Start to ungzip parameters.....");
} GZIPInputStream gis = new GZIPInputStream(ins);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); int b;
while((b = gis.read()) != -1){
baos.write(b);
} byte[] result = baos.toByteArray(); if(logger.isInfoEnabled()){
logger.info("Ungzip parameters bytes OK, result bytes size is: " + result.length);
} try{
gis.close();
}catch(IOException e){
logger.warn("Close GZIPInputStream fail:", e);
} return result;
} /**
* 对输入流的数据解压缩
* @param ins 待解压的输入流
* @param os 解压后的输出流
* @throws IOException
*/
public static void unGzip(InputStream ins, OutputStream os) throws IOException{ GZIPInputStream gis = new GZIPInputStream(ins); int b;
while((b = gis.read()) != -1){
os.write(b);
} try{
gis.close();
}catch(IOException e){
logger.warn("Close GZIPInputStream fail:", e);
}
} /**
* 把从gzip流中解压出来的参数和request原有的参数合并成一个新Map返回
*
* 客户端传来的数据没有经过url编码, 不需要解码
* @param data 解压后的字节数组
* @param res 请求的request
* @return 完整的parameterMap
* @throws UnsupportedEncodingException
*/
public static Map<String, Object> getParameterMap(byte[] data, ServletRequest request) throws UnsupportedEncodingException{
String body = null; if(data != null && data.length > 0){
body = new String(data, ENCODING);
} if(body != null && !"".equals(body.trim())){ if(logger.isInfoEnabled()){
logger.info("Ungzip parameters string is : " + body);
} //新parameterMap
Map<String, Object> paramsMap = new LinkedHashMap<String, Object>();
//把原有parameterMap添加到新map中
if(request.getParameterMap() != null){
paramsMap.putAll(request.getParameterMap());
} String splitMapFlag = "&";
String spiltKeyValueFlag = "="; String[] mapArr = body.split(splitMapFlag);
String[] arr = null;
for(String tagValue : mapArr){
arr = tagValue.split(spiltKeyValueFlag, 2);
if(arr.length == 2){
if(paramsMap.containsKey(arr[0]) && paramsMap.get(arr[0]) != null){
// List<String> list = new ArrayList<String>();
// list.addAll(Arrays.asList((String[])paramsMap.get(arr[0])));
// list.add(arr[1]);
// paramsMap.put(arr[0], list.toArray(new String[list.size()])); // String[] oldArr = (String[])paramsMap.get(arr[0]);
// String[] newArr = new String[oldArr.length + 1];
// System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
// newArr[newArr.length - 1] = arr[1];
// paramsMap.put(arr[0], newArr); String[] array = (String[])paramsMap.get(arr[0]);;
array = Arrays.copyOf(array, array.length + 1);
array[array.length - 1] = arr[1];
paramsMap.put(arr[0], array);
}else{
paramsMap.put(arr[0], new String[]{arr[1]});
}
}
}
return paramsMap;
} return request.getParameterMap();
}
修改filter类, 同时兼容不支持gzip压缩的老版本客户端:
//判断返回给客户端的响应流是否是需要经过gzip压缩
String acceptEncoding = req.getHeader(GZipUtil.ACCEPT_ENCODING);
if (acceptEncoding != null
&& acceptEncoding.trim().equalsIgnoreCase(GZipUtil.ENCODING_GZIP))
{
res.setHeader(GZipUtil.CONTENT_ENCOING, GZipUtil.ENCODING_GZIP);
GZipResponse gzipResponse = new GZipResponse(res); //contentType为application/x-gzip, 代表客户端压缩了请求参数, 需要解压
String contentType = req.getContentType();
if (contentType != null
&& contentType.equalsIgnoreCase(GZipUtil.MIME_APPLICATION_X_GZIP))
{
byte[] data = GZipUtil.unGzip(req.getInputStream());
Map<String, Object> newParameterMap = GZipUtil.getParameterMap(data,
req);
RequestParameterWrapper requestWrapper = new RequestParameterWrapper(
req, newParameterMap);
chain.doFilter(requestWrapper, gzipResponse);
}
else
{
chain.doFilter(req, gzipResponse);
}
}
else
{
chain.doFilter(request, res);
}
因为请求内容有被压缩的post参数未正常保存在request中, 建立新类RequestParameterWrapper(继承HttpServletRequestWrapper, 调用gzipUtil的方法处理)重新包装请求参数, 同时使用GZipResponse extends HttpServletResponseWrapper对响应数据进行gzip压缩.
RequestParameterWrapper:
public class RequestParameterWrapper extends HttpServletRequestWrapper
{
private final Map params;
public RequestParameterWrapper(HttpServletRequest request, Map newParams)
{
super(request);
this.params = newParams;
} @Override
public Map getParameterMap(){
return params;
} @Override
public Enumeration getParameterNames(){
Vector l = new Vector(params.keySet());
return l.elements();
} @Override
public String[] getParameterValues(String name){
Object v = params.get(name);
if(v == null){
return null;
}else if(v instanceof String[]){
return (String[])v;
}else if(v instanceof String){
return new String[]{(String)v};
}else{
return new String[]{v.toString()};
}
} @Override
public String getParameter(String name){
Object v = params.get(name);
if(v == null){
return null;
}else if(v instanceof String[]){
return ((String[])v)[0];
}else if(v instanceof String){
return (String)v;
}else{
return v.toString();
}
} }
GZipResponse:
public class GZipResponse extends HttpServletResponseWrapper
{
private GZIPServletStream wrappedOut; public GZipResponse(HttpServletResponse response) throws IOException
{
super(response);
wrappedOut = new GZIPServletStream(response);
} public ServletOutputStream getOutputStream() throws IOException
{
return wrappedOut;
} private PrintWriter wrappedWriter; public PrintWriter getWriter() throws IOException
{
if (wrappedWriter == null)
{
wrappedWriter = new PrintWriter(new OutputStreamWriter(
getOutputStream(), getCharacterEncoding()));
}
return wrappedWriter;
} public void flush() throws IOException
{
if (wrappedWriter != null)
{
wrappedWriter.flush();
}
} private class GZIPServletStream extends ServletOutputStream
{
private HttpServletResponse response;
private OutputStream outputStream;
private ByteArrayOutputStream baos = new ByteArrayOutputStream(); public GZIPServletStream(HttpServletResponse response) throws IOException
{
this.response = response;
outputStream = response.getOutputStream();
} public void write(byte[] buf) throws IOException
{
baos.write(buf);
} public void write(byte[] buf, int off, int len) throws IOException
{
baos.write(buf, off, len);
} public void write(int c) throws IOException
{
baos.write(c);
} public void write(byte buf) throws IOException
{
baos.write(buf);
} public void flush() throws IOException
{
// byte[] bytes = baos.toByteArray();
// GZipUtil.gzip(bytes, outputStream); //需要把压缩后的字节大小设置到response的content-length中
byte[] bytes = GZipUtil.gzip(baos.toByteArray());
outputStream.write(bytes); outputStream.flush();
response.setHeader(GZipUtil.CONTENT_LENGTH, String.valueOf(bytes.length));
} public void close() throws IOException
{
outputStream.close();
}
}
}
Java Web 减少网络 IO、静态资源磁盘 IO 有效的办法--响应使用 GZIP( 压缩http请求与响应gzip压缩)的更多相关文章
- 使用java调用fastDFS客户端进行静态资源文件上传
一.背景 上篇博客我介绍了FastDFS的概念.原理以及安装步骤,这篇文章我们来聊一聊如何在java中使用FastDFSClient进行静态资源的上传. 二.使用步骤 1.开发环境 spring+sp ...
- 013-Spring Boot web【二】静态资源、Servlet、Filter、listenter
一.静态资源 1.1.webapp默认支持静态资源 在src/main/webapp下建立user.html默认支持访问 1.2.默认内置静态资源目录.可被直接访问 查看包:spring-boot-a ...
- [系统资源攻略]IO第一篇-磁盘IO,内核IO概念
几个基本的概念 在研究磁盘性能之前我们必须先了解磁盘的结构,以及工作原理.不过在这里就不再重复说明了,关系硬盘结构和工作原理的信息可以参考维基百科上面的相关词条--Hard disk drive(英文 ...
- idea 修改静态资源不需要重启的办法
快捷键Ctrl + Alt + S打开设置面板,勾选Build project automatically选项: 快捷键Ctrl + Shift + A查找registry命令: 在查找到的regis ...
- SpringMVC中css,js,图片等静态资源被拦截的解决办法
一.静态资源的存放路径 css,js,图片等静态资源存放在项目的路径必须为 二.html.jsp导入静态资源文件 html.jsp页面中的导入静态资源文件: js: css: 图片: 二.web.xm ...
- maven的java web项目启动找不到Spring ContextLoaderListener的解决办法
用maven搭建的java web项目,上传到git仓库后,当同事clone下来项目,部署到tomcat运行时,就报了如下错误,即启动web项目时,加载web.xml文件,找不到spring的监听器, ...
- spring boot jsp里面的静态资源访问不到解决办法
闲着没事写的小Demo 用到了jsp页面,里面有些静态资源, springboot 默认的静态资源的值有四个:Default: classpath:/META-INF/resources/,class ...
- java Web jsp页面的静态包含和动态包含
现在有头 体 尾 三个jsp页面 top.jsp <%@ page language="java" contentType="text/html; charset= ...
- java web jsp原理图 ,静态包含,动态包含,out与response.getWrite()
jsp原理图 ,静态包含,动态包含,out与response.getWrite()
随机推荐
- Asp.net WebApi 配置 Swagger UI
首先安装Swashbuckle.Core 然后添加swagger配置文件. [assembly: PreApplicationStartMethod(typeof(SwaggerConfig), &q ...
- cocos2d-x入门一
为什么要学cocos2d-x 首先要明白什么是cocos2d-x,能干什么.cocos2d-x是一种跨平台的2D.3D游戏开发工具,目前较为流行的大多数android.ios游戏都是用它 ...
- 解决httpclient因为保持永久长连接造成连接吊死的问题
httpclient使用了连接池,如果没有设置keep-alive策略,PoolingHttpClientConnectionManager会默认使用永久连接. 最近在调用京东api时,发现一个请求开 ...
- SQL SERVER连接池
Connection Pool 是什么呢 ?每当程序需要读写数据库的时候.Connection.Open()会使用ConnectionString连接到数据库,数据库会为程序建立 一个连接,并且保持打 ...
- MySQL之长连接、短连接、连接池
当数据库服务器和客户端位于不同的主机时,就需要建立网络连接来进行通信.客户端必须使用数据库连接来发送命令和接收应答.数据.通过提供给客户端数据库的驱动指定连接字符串后,客户端就可以和数据库建立连接了. ...
- ItemsControl的ItemContainerStyle属性
ItemsControl:ListBox,ComboBox,TreeView ItemContainerStyle是用来设置每一个集合控件的Item的样式的属性(即设置每一个项的样式). 使用It ...
- ACM比赛辅导--授课内容
Lesson1(3月19日) 1.讲解Dev-C++.VC++6.0的单步调试方法 2.学习比赛的基本输入输出,练习C语言网的1085—1092 Lesson2(3月21日) 1.学习挑战程序设计,第 ...
- IDEA的Tomcat配置Web的项目创建以及Servlet简单运行。
相关软件: 1.IDEA编译器 2.JDK 3.Tomcat (相关软件都可以到官网上下载,老表提示:不要下载最新版本因为不要做试验品) IDEA的安装非常简单,找好安装的盘,n ...
- vue-awesome-swiper 第一张自动跳过
昨天在上班中要做一个商品页面,需求是从后台接口获得轮播图的路径,然后传到封装好的组件中,本来以为很简单啊,没什么毛病,开始动手~ 东西很简单,新建一个banner组件 如下: <template ...
- 【转载】deque双向队列
继vector和queue之后,又发现一个很好用的东西. 本篇转载自http://blog.csdn.net/morewindows/article/details/6946811 deque双向队列 ...