JSP Filter,GZIP压缩响应流

url:http://hi.baidu.com/xhftx/blog/item/fbc11d3012648711ebc4af59.html 
关键词:JSP,Filter,Servlet,GZIP 
现在主流浏览器都是支持gzip的。服务器压缩网页后进行传输,可减少传输数据的大小使用户感觉访问速度更快。当然,压缩也会消耗一部分服务器处理时间。 
用Filter实现对返回信息的压缩,代码参考Tomcat examples里面的compressionFilters: 
GZipStream.java

  1. import java.io.IOException;
  2. import java.io.OutputStream;
  3. import java.util.zip.GZIPOutputStream;
  4. import javax.servlet.ServletOutputStream;
  5. public class GZipStream extends ServletOutputStream {
  6. private GZIPOutputStream zipStream;
  7. public GZipStream(OutputStream out) throws IOException {
  8. zipStream = new GZIPOutputStream(out);
  9. }
  10. @Override
  11. public void flush() throws IOException {
  12. zipStream.flush();
  13. }
  14. @Override
  15. public void write(byte[] b, int off, int len) throws IOException {
  16. zipStream.write(b, off, len);
  17. }
  18. @Override
  19. public void write(byte[] b) throws IOException {
  20. zipStream.write(b);
  21. }
  22. @Override
  23. public void write(int arg0) throws IOException {
  24. zipStream.write(arg0);
  25. }
  26. public void finish() throws IOException {
  27. zipStream.finish();
  28. }
  29. public void close() throws IOException {
  30. zipStream.close();
  31. }
  32. }

GZipResponse.java

  1. import java.io.IOException;
  2. import java.io.OutputStreamWriter;
  3. import java.io.PrintWriter;
  4. import javax.servlet.ServletOutputStream;
  5. import javax.servlet.http.HttpServletResponse;
  6. import javax.servlet.http.HttpServletResponseWrapper;
  7. public class GZipResponse extends HttpServletResponseWrapper {
  8. private GZipStream stream;
  9. private PrintWriter writer;
  10. public GZipResponse(HttpServletResponse response) throws IOException{
  11. super(response);
  12. stream=new GZipStream(response.getOutputStream());
  13. }
  14. @Override
  15. public ServletOutputStream getOutputStream() throws IOException {
  16. return stream;
  17. }
  18. @Override
  19. public PrintWriter getWriter() throws IOException {
  20. if (writer == null) {
  21. writer = new PrintWriter(new OutputStreamWriter(
  22. getOutputStream(), getCharacterEncoding()));
  23. }
  24. return writer;
  25. }
  26. public void flush() throws IOException {
  27. if (writer != null) {
  28. writer.flush();
  29. }
  30. stream.finish();
  31. }
  32. }

GZipFilter.java

  1. import java.io.IOException;
  2. import javax.servlet.Filter;
  3. import javax.servlet.FilterChain;
  4. import javax.servlet.FilterConfig;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.ServletRequest;
  7. import javax.servlet.ServletResponse;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. public class GZipFilter implements Filter {
  11. public void destroy() {
  12. }
  13. public void init(FilterConfig fConfig) throws ServletException {
  14. }
  15. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  16. throws IOException, ServletException {
  17. HttpServletRequest req=(HttpServletRequest)request;
  18. HttpServletResponse res=(HttpServletResponse)response;
  19. if(isGZipEncoding(req)){
  20. GZipResponse zipResponse=new GZipResponse(res);
  21. res.setHeader("Content-Encoding", "gzip");
  22. chain.doFilter(request, zipResponse);
  23. zipResponse.flush();
  24. } else {
  25. chain.doFilter(request, response);
  26. }
  27. }
  28. /**
  29. * 判断浏览器是否支持GZIP
  30. * @param request
  31. * @return
  32. */
  33. private static boolean isGZipEncoding(HttpServletRequest request){
  34. boolean flag=false;
  35. String encoding=request.getHeader("Accept-Encoding");
  36. ){
  37. flag=true;
  38. }
  39. return flag;
  40. }
  41. }

web.xml配置

  1. <filter>
  2. <description>
  3. </description>
  4. <display-name>GZipFilter</display-name>
  5. <filter-name>GZipFilter</filter-name>
  6. <filter-class>GZipFilter</filter-class>
  7. </filter>
  8. <filter-mapping>
  9. <filter-name>GZipFilter</filter-name>
  10. <url-pattern>/*</url-pattern>
  11. </filter-mapping>

这个配置是对所有的资源都进行压缩传输,对于图片,flash等本身已经压缩过的文件就没有必要再进行压缩了,可以根据自己的需要更改<url-pattern>已提高WEB访问速度。

tomcat自带源码剖析: 
url:http://hi.baidu.com/springfieldx/blog/item/9faa88dfd5760414495403b6.html 
在响应请求的时候对response进行封装,替换他的输出流为 GZipOutputStream 压缩输出流

  1. package compressionFilters;
  2. import java.io.IOException;
  3. import java.util.zip.GZIPOutputStream;
  4. import javax.servlet.ServletOutputStream;
  5. import javax.servlet.http.HttpServletResponse;
  6. public class CompressionResponseStream extends ServletOutputStream {
  7. //是否启用压缩的临界值
  8. ;
  9. //临时容纳写入的数据缓冲区
  10. protected byte[] buffer = null;
  11. //缓冲区实际写入的数据量
  12. ;
  13. protected GZIPOutputStream gzipstream = null;
  14. //当前流对象是否处于关闭状态
  15. protected boolean closed = false;
  16. ;
  17. //用于返回数据的 Response对象
  18. protected HttpServletResponse response = null;
  19. protected ServletOutputStream output = null;
  20. public CompressionResponseStream(HttpServletResponse response) throws IOException{
  21. super();
  22. closed = false;
  23. this.response = response;
  24. this.output = response.getOutputStream();
  25. }
  26. //设置启用压缩的临界值,并初始化输出缓冲区
  27. public void setBuffer(int threshold){
  28. compressThreshold = threshold;
  29. buffer = new byte[threshold];
  30. }
  31. /**
  32. * 关闭流对象
  33. */
  34. public void close() throws IOException{
  35. if(closed){
  36. throw new IOException("This output stream has already been closed");
  37. }
  38. //如果当前启用的是压缩流,用压缩流刷新缓冲区
  39. if(gzipstream != null){
  40. flushToGZip();
  41. gzipstream.close();
  42. gzipstream = null;
  43. }else{
  44. //如果未开启压缩,则用response的默认输出流刷新缓冲区
  45. ){
  46. , bufferCount);
  47. ;
  48. }
  49. }
  50. }
  51. /**
  52. * 刷新输出缓冲区
  53. */
  54. public void flush() throws IOException{
  55. if(closed){
  56. throw new IOException("Cannot flush a closed output stream");
  57. }
  58. if(gzipstream != null){
  59. gzipstream.flush();
  60. }
  61. }
  62. public void write(int b) throws IOException {
  63. if(closed){
  64. throw new IOException("Cannot write to a closed output stream");
  65. }
  66. //如果数据超出了缓冲区(启用压缩的临界值),则启用压缩模式
  67. if(bufferCount >= buffer.length){
  68. flushToGZip();
  69. }
  70. //如果没有超出缓冲区,则将数据写入缓冲区
  71. buffer[bufferCount++] = (byte)b;
  72. }
  73. public void write(byte[] b,int off,int len) throws IOException{
  74. if(closed){
  75. throw new IOException("Cannot write to a closed output stream");
  76. }
  77. ){
  78. return;
  79. }
  80. //如果缓冲区能容纳这些数据则写入缓冲区
  81. if(len <= buffer.length - bufferCount){
  82. System.arraycopy(b, off, buffer, bufferCount, len);
  83. bufferCount += len;
  84. return;
  85. }
  86. //如果缓冲区剩余空间不住足,则开启压缩流对象
  87. flushToGZip();
  88. //如果清空后的缓冲区能够容纳传送过来的数据,则将数据写入缓冲区
  89. if(len <= buffer.length - bufferCount){
  90. System.arraycopy(b, off, buffer, bufferCount, len);
  91. bufferCount += len;
  92. return;
  93. }
  94. //如果缓冲区不能容纳传送进来的数据,则直接将数据写入压缩流对象
  95. writeToGZip(b, off, len);
  96. }
  97. //刷新压缩流对象的缓冲区
  98. public void flushToGZip() throws IOException{
  99. ){
  100. , bufferCount);
  101. ;
  102. }
  103. }
  104. public void writeToGZip(byte b[],int off,int len)throws IOException{
  105. //如果压缩流对象为空,这里初始化它
  106. if(gzipstream == null){
  107. response.addHeader("Content-Encoding", "gzip");
  108. gzipstream = new GZIPOutputStream(output);
  109. }
  110. //将数据听过压缩流对象输出到response的输出流
  111. gzipstream.write(b,off,len);
  112. }
  113. public boolean closed(){
  114. return closed;
  115. }
  116. }
  117. package compressionFilters;
  118. import java.io.IOException;
  119. import java.util.zip.GZIPOutputStream;
  120. import javax.servlet.ServletOutputStream;
  121. import javax.servlet.http.HttpServletResponse;
  122. public class CompressionResponseStream extends ServletOutputStream {
  123. //是否启用压缩的临界值
  124. ;
  125. //临时容纳写入的数据缓冲区
  126. protected byte[] buffer = null;
  127. //缓冲区实际写入的数据量
  128. ;
  129. protected GZIPOutputStream gzipstream = null;
  130. //当前流对象是否处于关闭状态
  131. protected boolean closed = false;
  132. ;
  133. //用于返回数据的 Response对象
  134. protected HttpServletResponse response = null;
  135. protected ServletOutputStream output = null;
  136. public CompressionResponseStream(HttpServletResponse response) throws IOException{
  137. super();
  138. closed = false;
  139. this.response = response;
  140. this.output = response.getOutputStream();
  141. }
  142. //设置启用压缩的临界值,并初始化输出缓冲区
  143. public void setBuffer(int threshold){
  144. compressThreshold = threshold;
  145. buffer = new byte[threshold];
  146. }
  147. /**
  148. * 关闭流对象
  149. */
  150. public void close() throws IOException{
  151. if(closed){
  152. throw new IOException("This output stream has already been closed");
  153. }
  154. //如果当前启用的是压缩流,用压缩流刷新缓冲区
  155. if(gzipstream != null){
  156. flushToGZip();
  157. gzipstream.close();
  158. gzipstream = null;
  159. }else{
  160. //如果未开启压缩,则用response的默认输出流刷新缓冲区
  161. ){
  162. , bufferCount);
  163. ;
  164. }
  165. }
  166. }
  167. /**
  168. * 刷新输出缓冲区
  169. */
  170. public void flush() throws IOException{
  171. if(closed){
  172. throw new IOException("Cannot flush a closed output stream");
  173. }
  174. if(gzipstream != null){
  175. gzipstream.flush();
  176. }
  177. }
  178. public void write(int b) throws IOException {
  179. if(closed){
  180. throw new IOException("Cannot write to a closed output stream");
  181. }
  182. //如果数据超出了缓冲区(启用压缩的临界值),则启用压缩模式
  183. if(bufferCount >= buffer.length){
  184. flushToGZip();
  185. }
  186. //如果没有超出缓冲区,则将数据写入缓冲区
  187. buffer[bufferCount++] = (byte)b;
  188. }
  189. public void write(byte[] b,int off,int len) throws IOException{
  190. if(closed){
  191. throw new IOException("Cannot write to a closed output stream");
  192. }
  193. ){
  194. return;
  195. }
  196. //如果缓冲区能容纳这些数据则写入缓冲区
  197. if(len <= buffer.length - bufferCount){
  198. System.arraycopy(b, off, buffer, bufferCount, len);
  199. bufferCount += len;
  200. return;
  201. }
  202. //如果缓冲区剩余空间不住足,则开启压缩流对象
  203. flushToGZip();
  204. //如果清空后的缓冲区能够容纳传送过来的数据,则将数据写入缓冲区
  205. if(len <= buffer.length - bufferCount){
  206. System.arraycopy(b, off, buffer, bufferCount, len);
  207. bufferCount += len;
  208. return;
  209. }
  210. //如果缓冲区不能容纳传送进来的数据,则直接将数据写入压缩流对象
  211. writeToGZip(b, off, len);
  212. }
  213. //刷新压缩流对象的缓冲区
  214. public void flushToGZip() throws IOException{
  215. ){
  216. , bufferCount);
  217. ;
  218. }
  219. }
  220. public void writeToGZip(byte b[],int off,int len)throws IOException{
  221. //如果压缩流对象为空,这里初始化它
  222. if(gzipstream == null){
  223. response.addHeader("Content-Encoding", "gzip");
  224. gzipstream = new GZIPOutputStream(output);
  225. }
  226. //将数据听过压缩流对象输出到response的输出流
  227. gzipstream.write(b,off,len);
  228. }
  229. public boolean closed(){
  230. return closed;
  231. }
  232. }
  233. package compressionFilters;
  234. import java.io.IOException;
  235. import java.io.OutputStream;
  236. import java.io.OutputStreamWriter;
  237. import java.io.PrintWriter;
  238. import javax.servlet.ServletOutputStream;
  239. import javax.servlet.http.HttpServletResponse;
  240. import javax.servlet.http.HttpServletResponseWrapper;
  241. public class CompressionServletResponseWrapper extends
  242. HttpServletResponseWrapper {
  243. /**
  244. * 原始的response对象
  245. */
  246. protected HttpServletResponse origResponse = null;
  247. protected static final String info = "CompressionServletResponseWrapper";
  248. /**
  249. * response对象的输出流
  250. */
  251. protected ServletOutputStream stream = null;
  252. /**
  253. * response
  254. */
  255. protected PrintWriter writer = null;
  256. ;
  257. protected String contentType = null;
  258. public CompressionServletResponseWrapper(HttpServletResponse response) {
  259. super(response);
  260. origResponse = response;
  261. }
  262. /**
  263. * 设置实体类型
  264. */
  265. public void setContentType(String contentType){
  266. this.contentType = contentType;
  267. origResponse.setContentType(contentType);
  268. }
  269. /**
  270. * 设置启用压缩的临界值
  271. * @param threshold 临界值
  272. */
  273. public void setCompressionThreshold(int threshold){
  274. this.threshold = threshold;
  275. }
  276. public ServletOutputStream createOutputStream() throws IOException{
  277. CompressionResponseStream stream = new CompressionResponseStream(origResponse);
  278. stream.setBuffer(threshold);
  279. return stream;
  280. }
  281. //关闭输出对象
  282. public void finishResponse(){
  283. try {
  284. if(writer != null){
  285. writer.close();
  286. }else{
  287. if(stream != null){
  288. stream.close();
  289. }
  290. }
  291. } catch (IOException e) {
  292. }
  293. }
  294. public void flushBuffer() throws IOException{
  295. ((CompressionResponseStream)stream).flush();
  296. }
  297. public ServletOutputStream getOutputStream() throws IOException{
  298. //如果字符流打开,则抛出异常
  299. if(writer != null)
  300. throw new IllegalStateException("getWriter() has already been called for this response");
  301. if(stream == null) stream = createOutputStream();
  302. return stream;
  303. }
  304. public PrintWriter getWriter() throws IOException{
  305. if(writer != null){
  306. return (writer);
  307. }
  308. if(stream != null)
  309. throw new IllegalStateException("getOutputStream() has already been called for this response");
  310. stream = createOutputStream();
  311. String charEnc = origResponse.getCharacterEncoding();
  312. if(charEnc != null){
  313. writer = new PrintWriter(new OutputStreamWriter(stream,charEnc));
  314. }else{
  315. writer = new PrintWriter(stream);
  316. }
  317. return writer;
  318. }
  319. private static String getCharsetFromContentType(String type){
  320. if(type == null){
  321. return null;
  322. }
  323. int semi = type.indexOf(":");
  324. ){
  325. return null;
  326. }
  327. );
  328. int charsetLocation = afterSemi.indexOf("charset=");
  329. ){
  330. return null;
  331. }else{
  332. );
  333. String encoding = afterCharset.trim();
  334. return encoding;
  335. }
  336. }
  337. }

reference: 
1.http://onjava.com/pub/a/onjava/2003/11/19/filters.html 
2.java filter 过滤机制详解 http://hi.baidu.com/linfengtingyu1/blog/item/e14a1af20de0505b352accda.html 
3.java.servlet.Filter的应用http://yuhaining.spaces.live.com/Blog/cns!5BBD70DF0F6F839C!307.entry 
4.使用filter机制来GZIP压缩网页http://www.javamilk.cn/article/notes/624.htm

上一篇:ubuntu的目录结构


下一篇:MVC 异常过滤