Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

ASP.NET Performance – Part 3a – Better Cache Busting

There have been some good, informative, comments to this series. One in particular, from Dan Sargeant, deserves to be addressed directly – in large part because its something I overlooked but need to implement, so I might as well share the code.

Dan points out that my cache busting technique has a fatal flaw – it doesn’t address images referenced from stylesheet. He’s right, and it’s a pretty important issue to fix for two reasons. First, a lot of images are references from stylesheets, secondly, we’re returning a long expiry headers for all of those assets, so we really need a way to bust the cache.

The solution is simple, we’ll take the code introduced in Part 3 which calculates the hashes and generates our data file, and add a 3rd step which relies on regular expressions to update images referenced in stylesheets. First we define our regular expressions:

private static readonly Regex _stylesheetPattern = new Regex("'\\.\\./images/(.*?)(\\?.*?)?'", RegexOptions.Compiled);

This assumes that your structure looks something like:

assets/css/main.css
assets/images/background.gif

meaning that your image references will look something like:

#main{background:url('../images/background.gif');}

Next we run the code which’ll find all .css files and do a search and replace:

private static void ProcessStyleSheets(string root)
{
   foreach(var directory in Directory.GetDirectories(root))
   {
      //recurse
      ProcessStyleSheets(directory);
   }
   foreach(var file in Directory.GetFiles(root, "*.css"))
   {
      var content = File.ReadAllText(file);
      content = _stylesheetPattern.Replace(content, new MatchEvaluator(ApplyHashToMatchedStyle));
      File.WriteAllText(file, content);
   }
}
 
private static string ApplyHashToMatchedStyle(Match match)
{
   var key = string.Concat("/assets/images/", match.Groups[1].Value);
   if (_hashes.ContainsKey(key))
   {
      return string.Format("'../images/{0}?{1}'", match.Groups[1].Value, _hashes[key]);
   }
   return match.Groups[0].Value;
}

Finally we change our main program to call the ProcessStyleSheets method:

...
_hashes = new Dictionary<string, string>();  
ProcessDirectory(root, assetPath);  
WriteFile(root + outputFile);  
ProcessStyleSheets(root + assetPath);

I also want to mention that Herman, who had a number of valuable comments, pointed out that, by default, some proxies won’t cache based on querystring (Squid being the best known). Instead, we need to make our hash part of filename. This is something I’ll leave to you to play with, but I will point out that this is something Nginx’s rewriteurl module would be very well suited for.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

One Response to ASP.NET Performance – Part 3a – Better Cache Busting

  1. Dan Sargeant says:

    Thanks Karl, make sense now. This method of hashing the file will definately be something I shall be considering using.