前言
最近的做的项目中,遇到这么一个问题:服务器返回给客户端的xml报文数据量太大,费时耗流量,于是要求服务端添加gzip支持,现在把整个流程写下来,以供以后有需要时参考。gzip是一种文件压缩算法,http服务器端添加支持后,客户端请求时添加请求头信息Accept-Encoding:gzip即可享受服务端的gzip压缩服务,获取压缩后的数据,极大减小网络传输数据,提高响应速度和减少流量。当然,如果客户端http请求中没有添加Accept-Encoding:gzip请求头信息,服务器端返回的依旧是未压缩的数据。
服务端gzip支持
首先,写一个简单的servlet程序,读取一个文本文件,然后返回给客户端
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException {
String xmlFileName = "books.xml";
try {
response.setContentType("text/html;charset=utf-8");
String filePath=this.getServletConfig().getServletContext().getRealPath("/"+xmlFileName);
File file = new File(filePath);
//文件原本大小
System.out.println("服务端文件大小:"+file.length()+"byte");
FileInputStream fileIn =new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int i = 0;
while ((i = fileIn.read(b)) != -1) {
baos.write(b, 0, i);
}
ServletOutputStream out = response.getOutputStream();
out.write(baos.toByteArray());
out.flush();
out.close();
fileIn.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
然后,tomcat配置gzip支持,具体做法是在server.xml配置文件中添加以下属性
compression:开启压缩,可选值:"on"开启,"off"关闭,"force"任何情况都开启
compressionMinSize:指定压缩的最小数据大小,单位B,默认2048B
noCompressionUserAgents:正则表达式,匹配的UA将不会被压缩,默认空
compressableMimeType:会被压缩的MIME类型列表,多个逗号隔开,默认值:text/html,text/xml,text/plain,text/css,text/javascript,application/javascript
具体参考官网: http://tomcat.apache.org/tomcat-7.0-doc/config/http.html
客户端请求及解压缩
首先,通过HttpURLConnection发起请求,看一看未添加Accept-Encoding:gzip请求头的情况,代码如下
public static void main(String[] args) throws Exception{
StringBuffer url = new StringBuffer("http://127.0.0.1:8080/GZip/GetBook");
URL u = new URL(url.toString());
HttpURLConnection conn;
InputStream in;
conn = (HttpURLConnection) u.openConnection();
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
in = conn.getInputStream();
System.out.println("客户端获取数据大小:"+in.available()+"byte");
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
StringBuffer sb = new StringBuffer();
while((line = reader.readLine())!= null){
sb.append(line);
}
//打印获取到的数据
System.out.println(sb.toString());
in.close();
conn.disconnect();
}
控制台信息:
数据未压缩,数据大小一致
接着,再来看看添加Accept-Encoding:gzip请求头的情况,在上面的客户端代码添加下面一句
conn.addRequestProperty("Accept-Encoding", "gzip");
请求下:
数据已经被压缩,有原来的8365bB变成258B,gzip格式的数据显示为乱码,所有现在需要做的是对数据进行解压缩,使用JDK自带的GZIPInputStream就能搞定
GZIPInputStream gzipIn = new GZIPInputStream(in);
BufferedReader reader = new BufferedReader(new InputStreamReader(gzipIn));
结果如下
到此,整个流程基本结束
再插一脚
删除books.xml中的部分数据,使其大小小于配置的最小压缩数据2048B,再次请求,会发现数据不会被压缩
因为数据格式不是gzip压缩格式,所有使用GZIPInputStream解压缩抛异常了
还有一点,浏览器默认请求头包含Accept-Encoding:gzip,而且会自动解压缩,浏览器不会显示乱码,另外,对于请求头中的deflate和gzip类似,也是一种压缩算法,不过多介绍。
插曲
之前通过另一种方式写的服务端发生了件有意思的事,代码如下
PrintWriter out = response.getWriter();
BufferedReader br = new BufferedReader(new InputStreamReader(fileIn));
StringBuffer sb = new StringBuffer();
String line = null;
while ((line = br.readLine())!=null){
sb.append(line);
}
out.write(sb.toString());
然后发现客户端的数据大小总比服务端的少了点,找了好久才查出原因。原来是BufferedReader的readLine方法会把每行的回车换行去掉,UTF-8编码下CRLF各占一个字符,所有,数据大小自然变小了。真是浅草才能没马蹄啊,囧。
总结
本文主要写了一个tomcat配置gzip压缩和客户端解压缩的实例,对于不同数据大小下压缩率不尽相同,篇幅有限,不再描述,有兴趣的同学可以自行测试。