◆ 무한한 가능성/& JAVA

GZIP과 Filter을 활용한 웹컨텐츠(웹페이지) 압축방법

치로로 2013. 8. 13. 15:19


Googling: request.getHeader("Accept-Encoding"); gzip compression



Compressing web content with GZip and Filter



ref.> http://www.jroller.com/coreteam/entry/compressing_web_content_with_gzip




WEDNESDAY SEP 07, 2005

Compressing web content with GZip and Filter

I come across some performence problems in my recent j2ee web project. It always takes a long time for the visitors to wait to see the whole web page. Most of the web content is text-based. So I decide to compress the web content using gzip, which can dramatically reduce download times for text-based files. This can be done in a Filter.

First, check whether a browser supports gzip compression. Browsers that support this feature will set the Accept-Encoding request header.

/** Does the client support gzip? */
public boolean isGzipSupported(HttpServletRequest request) {
    String encodings = request.getHeader("Accept-Encoding");
 return ((encodings != null) && (encodings.indexOf("gzip") != -1));
}

If a browser supports gzip, we can then send gzip-compressed content to it. The following is the whole code:

package com.esurfer.filters;

import java.io.IOException;
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.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GzipFilter implements Filter {
 private FilterConfig config;

 public GzipFilter() {
 }

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

 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
        // If the browser supports gzip, send compressed content.
  // otherwise send the web content directly.
  PrintWriter out;
  if (isGzipSupported((HttpServletRequest) request)) {
   out = getGzipWriter((HttpServletResponse) response);
   ((HttpServletResponse) response).setHeader("Content-Encoding",
     "gzip");
   ResponseWrapper wrapper = new ResponseWrapper(
     (HttpServletResponse) response);
   chain.doFilter(request, wrapper);
   out.print(wrapper.toString());
   out.close();
  } else {
   chain.doFilter(request, response);
  }

 }

 public void destroy() {
  config = null;
 }

 /** Check the Accepting-Encoding header from the HTTP request. */
 public boolean isGzipSupported(HttpServletRequest request) {
  String encodings = request.getHeader("Accept-Encoding");
  return ((encodings != null) && (encodings.indexOf("gzip") != -1));
 }

 /** Return gzipping PrintWriter for response. */
 public PrintWriter getGzipWriter(HttpServletResponse response)
   throws IOException {
  return (new PrintWriter(
    new GZIPOutputStream(response.getOutputStream())));
 }

}

The java class used to wrap the response object:

package com.esurfer.filters;

import java.io.*;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class ResponseWrapper extends HttpServletResponseWrapper {
 StringWriter writer = new StringWriter();
 public ResponseWrapper(HttpServletResponse response) {
  super(response);
 }
 
 public PrintWriter getWriter() {
  return new PrintWriter(writer);
 }
 
 public String toString() {
  return writer.toString();
 }
}

To make it work, we need register the gzip filter in the deployment descriptor, and then map it to URL patterns.

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<display-name>GzipFilter</display-name>
<description>
GzipFilter Test
</description>
<filter>        
  <filter-name>Compress</filter-name>        
  <filter-class>com.esurfer.filters.GzipFilter</filter-class>        
</filter>  
<filter-mapping>        
  <filter-name>Compress</filter-name> 
  <url-pattern>*.html</url-pattern>    
</filter-mapping>
<filter-mapping>        
  <filter-name>Compress</filter-name> 
  <url-pattern>*.htm</url-pattern>    
</filter-mapping>
<filter-mapping>        
  <filter-name>Compress</filter-name> 
  <url-pattern>*.jsp</url-pattern>    
</filter-mapping>  
<filter-mapping>        
  <filter-name>Compress</filter-name> 
  <url-pattern>*.css</url-pattern>    
</filter-mapping>
<filter-mapping>        
  <filter-name>Compress</filter-name> 
  <url-pattern>*.js</url-pattern>    
</filter-mapping>
</web-app>

After finishing the steps above, we can examinate it using Web Page Analyzer provided by WebSiteOptimization.com. Which is a free website performance tool, and can be used to calculate page size, composition, and download time.

References

HTTP Compression http://www.port80software.com/products/httpzip/httpcompression

Compressing Web Content with mod_gzip and mod_deflate http://www.linuxjournal.com/article/6802

Servlet and JSP performance tuning  http://www.javaworld.com/javaworld/jw-06-2004/jw-0628-performance.html

Java Platform Performance: Strategies and Tactics http://java.sun.com/docs/books/performance/

How can you speed up webpage loading using Java Servlets? -- From HttpRevealer.com http://java.ittoolbox.com/pub/SC071902/httprevealer_servlets_itx.htm

Two Servlet Filters Every Web Application Should Have http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html?page=1

Comments:

I want to warn you that Apache Tomcat does not process filters for static resources (html, htm, css, js) by default. I have confronted with such problem during development AtLeap. In order to workaround this issue you can create your own servlet and map it on these extensions. These servlet will catch all requests and forward them to real DefaultServlet. 

The code you get from https://atleap.dev.java.net/source/browse/atleap/application/src/web/com/blandware/atleap/webapp/servlet/DefaultServlet.java 

Andrey Grebnev 
http://www.jroller.com/page/agrebnev 

Posted by Andrey Grebnev on September 07, 2005 at 08:37 PM CST #

One must be careful with gzip compression. We've encountered 2 problems with it in the past: 

1. Multipart MIME requests (i.e. file uploads) won't work in some situations. Sorry, but I am not sure what browser has this problem. I've heard of other people having this same problem. 

2. If you try to print a page with Netscape while using SSL + gzip you'll get garbage. This might be an older Netscape bug fixed in more recent versions. 

I suggest you use gzip on selected pages that contain a large amount of content.

Posted by Michael Slattery on September 07, 2005 at 09:49 PM CST #

Thanks a lot for your advice! I've tested the filter again. It is undoubtedly can't process the static resources.It seems that I must be careful to use gzip to compress the web content.

Posted by esurfer on September 07, 2005 at 10:26 PM CST #

Hello 

>chain.doFilter(request, wrapper); 
>out.print(wrapper.toString()); 

I suppose you do not use streaming here... 

What about mine: 
********** 
import javax.servlet.*; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import java.io.IOException; 
import java.util.Enumeration; 

public class GZIPFilter implements Filter 

public void init(FilterConfig filterConfig){} 
public void destroy() {} 

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

if (req instanceof HttpServletRequest && 
res instanceof HttpServletResponse) 

HttpServletRequest request = (HttpServletRequest)req; 

Enumeration e = request.getHeaders("Accept-Encoding"); 
while (e.hasMoreElements()) 

String name = (String)e.nextElement(); 

if (name != null && name.indexOf("gzip") != -1) 
{ // compress; 

HttpServletResponse response = (HttpServletResponse)res; 
GZIPResponseWrapper wr = new GZIPResponseWrapper(response); 

try 

chain.doFilter(request, wr); 

finally 

wr.closeBuffer(); 

return; 




chain.doFilter(req, res); 


********** 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpServletResponseWrapper; 
import javax.servlet.ServletOutputStream; 
import java.io.PrintWriter; 
import java.io.IOException; 
import java.io.OutputStreamWriter; 

public class GZIPResponseWrapper extends HttpServletResponseWrapper 

private GZIPServletOutputStream stream; 
private HttpServletResponse res; 
private PrintWriter writer; 

public GZIPResponseWrapper(HttpServletResponse res) 

super(res); 
this.res = res; 


public ServletOutputStream getOutputStream() throws IOException 

if (writer != null) 
throw new IllegalStateException("getWriter() has already been called for this response"); 

if (stream == null) 

res.addHeader("Content-Encoding", "gzip"); 
stream = new GZIPServletOutputStream(res.getOutputStream()); 


return stream; 


public PrintWriter getWriter() throws IOException 

if (stream != null) 
throw new IllegalStateException("getOutputStream() has already been called for this response"); 

if (writer == null) 

String enc = getCharacterEncoding(); 
writer = new PrintWriter(new OutputStreamWriter(getOutputStream(), enc)); 


return writer; 


public void flushBuffer() throws IOException 

if (writer != null) 
writer.flush(); 
else if (stream != null) 
stream.flush(); 


public void closeBuffer() throws IOException 

if (writer != null) 
writer.close(); 
else if (stream != null) 
stream.close(); 


*********** 
import javax.servlet.ServletOutputStream; 
import java.io.IOException; 
import java.io.BufferedOutputStream; 
import java.util.zip.GZIPOutputStream; 

public class GZIPServletOutputStream extends ServletOutputStream 

private GZIPOutputStream gzip; 

public GZIPServletOutputStream(ServletOutputStream out) throws IOException 

gzip = new GZIPOutputStream(new BufferedOutputStream(out)); 


public void write(int b) throws IOException 

gzip.write(b); 


public void write(byte b[]) throws IOException 

gzip.write(b, 0, b.length); 


public void write(byte b[], int off, int len) throws IOException 

gzip.write(b, off, len); 


public void close() throws IOException 

gzip.flush(); 
gzip.close(); 


public void flush() throws IOException 

gzip.flush(); 



Cheers, 

Dim

Posted by Dim on September 07, 2005 at 10:38 PM CST #

Thanks for your comment! Dim. I will review your code above thoroughly.

Posted by esurfer on September 07, 2005 at 11:19 PM CST #

What I don't understand is, in what respect is this solution superior to using the web servers build-in mechanism, such as mod_gzip oder tomcat's "compression" attribute in the "connector" configuration? 

Posted by Lutz Hühnken on September 14, 2005 at 06:13 PM CST #

Actually I see no one except of running something like weblogic over apache... :-) 
For small projects I just can't force people to install apache over their windowz (sometimes they complain even of java...). Company that uses linux usually has apache installed so in this situation it's easy with mod_gzip

Posted by Dim on September 14, 2005 at 11:28 PM CST #

bad

Posted by 68.33.107.135 on July 03, 2013 at 10:15 AM CST #