Thursday, November 12, 2009

new and improved super GZIP js/css with coldfusion

so a long time ago a blogged about using GZIP libraries in java to compress output on the fly. Since then i have made some tweaks to this technique.

1. I only have 2 SCRIPT tags in my pages.
<script src="/js/index.cfm?filenames=prototype.js,otherglobaljs.js....."
<script src="/js/index.cfm?filenames=jsonlyusedonthispage.js"

2. My index.cfm uses combine.cfc to compbine, minify and set the correct HTTP expiration headers on my code:

http://code.google.com/p/combine-cfc/

3. I gzip the resulting output back to the page if the browser supports it. See the full index.cfm below!

<cfsetting showdebugoutput="false" enablecfoutputonly="true">
<cfparam name="fileNames" default="">
<cfparam name="showRaw" default="false">


<cfset oCombine = createObject("component", "js.combine.combine").init(
enableCache: true,
cachePath: expandPath('output'),
enableETags: true,
enableJSMin: true,
enableYuiCSS: true,
skipMissingFiles: false,
enable304s: true
)>

<cfsavecontent variable="raw">
<cfset oCombine.combine(files=fileNames)>
</cfsavecontent>


<cfif cgi.HTTP_ACCEPT_ENCODING contains "gzip" AND not showRaw and Len(raw) GT 0>
<cfset bos = createObject("java","java.io.ByteArrayOutputStream") >
<cfset bos.init()>
<cfset gzipStream = createObject("java","java.util.zip.GZIPOutputStream")>
<cfset gzipStream.init(bos) >
<cfset gzipStream.write(raw.getBytes("iso-8859-1")) >
<cfset gzipStream.close()>
<cfset bos.flush()>
<cfset bos.close()>

<cfset encoder = createObject("java","sun.misc.BASE64Encoder")>
<cfset outStr= encoder.encode(bos.toByteArray())>
<cfset sOutput = toString(tobinary(outStr))>
<cfheader name="Content-Encoding" value="gzip" >
<cfheader name="Content-Length" value="#ArrayLen( sOutput.getBytes() )#" >
<cfoutput>#sOutput#</cfoutput>
<cfelse>
<cfoutput>#raw#</cfoutput>
</cfif>

Some notes:
You can use this for JS and/or CSS
notice i convert the result of combine into ISO before writing it to gzip. i think this was something i did to get around some strange problems with multi-lingual utf-8 content being coverted into base 64 encoding and back again.

1 comment:

Jon said...

I use those last two blocks of code (the ones doing the actual gzipping) within the Combine.cfc component in the outputContent function.

Just pass the sOut argument of that function to gzipStream.write() instead of the "raw" variable and you've have gzipped JS and CSS.

Doing it this way means you can call the default combine.cfm file that comes with the project.