6.3 Compressed Content

Java Servlet Programming, 2nd Edition > 6. Sending Multimedia Content > 6.3 Compressed Content

 
< BACKCONTINUE >

6.3 Compressed Content

The java.util.zip package was introduced in JDK 1.1. This package contains classes that support reading and writing the ZIP and GZIP compression formats. Although this package was added to support Java Archive (JAR) files, they also provide a convenient, standard way for a servlet to send compressed content.

Compressed content doesn't look any different to the end user because it's decompressed by the browser before it's displayed. Yet, while it looks the same, it can improve the end user's experience by reducing the time required to download the content from the server. For heavily compressible content such as HTML, compression can reduce transmission times by an order of magnitude. Quite a trick! Just bear in mind that to compress content dynamically forces the server to perform extra work, so any speedup in transmission time has to be weighed against slower server performance.

By now you should be familiar with the idea that a servlet can send a Content-Type header as part of its response to tell the client the type of information being returned. To send compressed content, a servlet must also send a Content-Encoding header to tell the client the scheme by which the content has been encoded. Under the HTTP 1.1 specification, the possible encoding schemes are gzip (or x-gzip), compress (or x-compress), and deflate.

Not all clients understand all encodings. To tell the server which encoding schemes it understands, a client may send an Accept-Encoding header that specifies acceptable encoding schemes as a comma-separated list. Not all browsers including a few that do support compressed encodings provide this header. For now, a servlet has to decide that without the header it won't send compressed content, or it has to examine the User-Agent header to see if the browser is one that supports compression. Of the current popular browsers, most but not all support gzip encoding, none supports compress encoding, and only Microsoft Internet Explorer 4 and 5 on Windows support deflate encoding. To easily determine the capabilities of a browser, including whether it supports GZIP, try the BrowserHawk4J product from Cyscape. This commercial product detects GZIP support as well as dozens of other interesting browser properties including browser type, browser version, client operating system, installed plug-ins, browser width and height, client monitor width and height, disabled cookies, disabled JavaScript, and even connection speed. See http://www.cyscape.com/products/bh4j.[5]

[5] Jason is proud that he was one of the principal developers hired to create BrowserHawk4J.

Although negotiating which compression format to use can involve a fair amount of logic, actually sending the compressed content could hardly be simpler. The servlet just wraps its standard ServletOutputStream with a GZIPOutputStream or ZipOutputStream. Be sure to call out.close() when your servlet is done writing output, so that the appropriate trailer for the compression format is written. Ah, the wonders of Java!

Example 6-12 shows the ViewResource servlet from Chapter 4 rewritten to send compressed content whenever possible. We'd show you a screen shot, but there's nothing new to see. As we said before, an end user cannot tell that the server sent compressed content to the browser except perhaps with reduced download times.

Example 6-12. Sending Compressed Content
import java.io.*; import java.net.*; import java.util.*; import java.util.zip.*; import javax.servlet.*; import javax.servlet.http.*; import com.oreilly.servlet.ServletUtils; public class ViewResourceCompress extends HttpServlet {   public void doGet(HttpServletRequest req, HttpServletResponse res)                                throws ServletException, IOException {     OutputStream out = null;     // Select the appropriate content encoding based on the     // client's Accept-Encoding header. Choose GZIP if the header     // includes "gzip". Choose ZIP if the header includes "compress".     // Choose no compression otherwise. Make sure the Content-Encoding     // uses the "x-" prefix if and only if the Accept-Encoding does.     String encodings = req.getHeader("Accept-Encoding");     if (encodings != null && encodings.indexOf("gzip") != -1) {       // Go with GZIP       if (encodings.indexOf("x-gzip") != -1) {         res.setHeader("Content-Encoding", "x-gzip");       }       else {         res.setHeader("Content-Encoding", "gzip");       }       out = new GZIPOutputStream(res.getOutputStream());     }     else if (encodings != null && encodings.indexOf("compress") != -1) {       // Go with ZIP       if (encodings.indexOf("x-compress") != -1) {         res.setHeader("Content-Encoding", "x-compress");       }       else {         res.setHeader("Content-Encoding", "compress");       }       out = new ZipOutputStream(res.getOutputStream());       ((ZipOutputStream)out).putNextEntry(new ZipEntry("dummy name"));     }     else {       // No compression       out = res.getOutputStream();     }     res.setHeader("Vary", "Accept-Encoding");     // Get the resource to view     URL url = null;     try {       url = ServletUtils.getResource(getServletContext(), req.getPathInfo());     }     catch (IOException e) {       res.sendError(         res.SC_NOT_FOUND,         "Extra path info must point to a valid resource to view: " +         e.getMessage());     }     // Connect to the resource     URLConnection con = url.openConnection();     con.connect();     // Get and set the type of the resource     String contentType = con.getContentType();     res.setContentType(contentType);     // Return the resource     try {       ServletUtils.returnURL(url, out);     }     catch (IOException e) {       res.sendError(res.SC_INTERNAL_SERVER_ERROR,               "Problem sending resource: " + e.getMessage());     }     // Write the compression trailer and close the output stream     out.close();   } }

The servlet begins by declaring a null OutputStream and then setting this OutputStream to a GZIPOutputStream, or ZipOutputStream, or ServletOutputStream, depending on the received Accept-Encoding header. As it selects which output stream to use, the servlet sets the Content-Encoding header accordingly. When sending compressed content, this header must be set for the client to run the appropriate decompression algorithm. The servlet also sets the Vary header to the value Accept-Encoding to be polite and indicate to the client that the servlet varies its output depending on the Accept-Encoding header. Most clients ignore this header.

After this early logic, the servlet can treat the output stream as just another OutputStream. It could wrap the stream with a PrintStream or PrintWriter, or it could pass it to a GifEncoder. But, no matter what it does, the servlet has to be sure to call out.close() when it's finished sending content. This call writes the appropriate trailer to the compressed stream.

There is some content that should not be compressed. For example, GIF and JPEG images are already compressed as part of their encoding, so there's not generally any benefit in compressing them again (and some browsers have a problem displaying images sent compressed). An improved version of the FileViewCompressed servlet would detect when it's returning an image and not bother with an attempt at further compression.


Last updated on 3/20/2003
Java Servlet Programming, 2nd Edition, © 2001 O'Reilly

< BACKCONTINUE >


Java servlet programming
Java Servlet Programming (Java Series)
ISBN: 0596000405
EAN: 2147483647
Year: 2000
Pages: 223

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net