In my previous post I discussed my AzureCdn.Me nuget package which can be used to add a cache busting query string to your CSS file references. In this post we look at cache busting image references contained in your CSS files, and minifying the result.
Cache Busters
Inexplicably the Azure CDN doesn’t ship with a big red reset button to allow you to clear the cache for the CDN. Meaning if you upload a new image or file that’s currently cached it may take hours or even days before the CDN refreshes the cache.
As I outlined in my previous post the easiest way round this is to add a query string to the resource reference, which differs on each deploy. Meaning the CDN will see the resource as changed and pull down a fresh copy.
All well and good, but as often as not your CSS file may actually contain image reference within, ie
.lolcat1 { background-image: url("./images/lolcat1.jpg"); background-size : 100% 100%; }
Now if you modify that image it won’t be updated on the CDN, which is bad news.
Introducing AzureCdn.Me.Nant
Any regular reader of my blog will know I’m a big fan of Nant. So I thought I’d write a build task to append a cache-busting query string onto the end of the image ref. I spent a bit of time investigating whether anyone else had addressed this problem, to my surprise I couldn’t find any.
In the course of that investigation I took a look at YUI Compressor. This open source tool minifies your CSS and Javascript. I downloaded the code, and realised I could enhance it to add a cache buster prior to doing the minification. Anyone interested can check out my fork on Github here.
Usage
I packaged up my changes as a nuget package AzureCdn.Me.Nant. You can install it into your build project.
Step 1 – You need to ensure all image references within your css are quoted, eg:
- good – url(“image.png”)
- good – url(‘image.png’)
- bad – url(image.png) – no quotes
If image refs aren’t quotes the YUI Compressor code won’t pick it up
Step 2 – Add a task similar to this into your build file, change the params to suit:
<loadtasks assembly=".\lib\Yahoo.Yui.Compressor.Build.Nant.dll" verbose="true" /> <target name="CompressCss"> <echo message="Compressing files"/> <cssCompressor deleteSourceFiles="false" outputFile="${buildspace.src.dir}/AzureCdnMe.Sample.Web/cdn/content/minified.css" compressionType="Standard" loggingType="Info" preserveComments="false" lineBreakPosition="-1" cdnQueryString="1.01" > <sourceFiles> <include name="..\AzureCdnMe.Sample.Web\cdn\content\azurecdnme.css" /> <include name="..\AzureCdnMe.Sample.Web\cdn\content\bootstrap.css" /> <include name="..\AzureCdnMe.Sample.Web\cdn\content\bootstrap-responsive.css" /> </sourceFiles> </cssCompressor> </target>
The new property I’ve added is cdnQueryString. When it runs it will both minify and cache bust your CSS image references, by appending a the supplied version number.
Referencing your code
Once you’ve minified the CSS you need to ensure your solution uses the new minified version. If you install my AzureCdn.Me package you can find a helper method that will allow you to determine if you are in debug mode or release, eg:
if (Html.IsInDebugMode()) { <link href="@Url.AzureCdnContent("~/Content/bootstrap.css")" rel="stylesheet" /> <link href="@Url.AzureCdnContent("~/Content/bootstrap-responsive.css")" rel="stylesheet" /> <link href="@Url.AzureCdnContent("~/Content/azurecdnme.css")" rel="stylesheet" /> } else { <link href="@Url.AzureCdnContent("~/Content/minified.css")" rel="stylesheet" type="text/css" /> }
Show me the code
You can find a working sample of the above here, where it’s hopefully obvious what is going on.
Conclusions
If anyone is interested it would be easy to enhance the YUI Compressor code to add an MSBuild task to add the cdnQueryString parameter. Also the cdnQueryString param will work in the same way if you want to also minify any javascript.