Skip to content

Instantly share code, notes, and snippets.

@tuespetre
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuespetre/770f6ab6d35199277e4c to your computer and use it in GitHub Desktop.
Save tuespetre/770f6ab6d35199277e4c to your computer and use it in GitHub Desktop.
An MVC helper class for adding hashes to versioned files (css, js, images, etc.)
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace Your.Project.Helpers
{
public static class VersionedStaticFileHelper
{
public static string Versioned(this UrlHelper helper, string path, string versionParameter = "v")
{
var fullPath = helper.RequestContext.HttpContext.Server.MapPath(path);
var file = new FileInfo(fullPath);
if (!file.Exists)
{
return helper.Content(path);
}
var versioned = default(VersionedFileInfo);
if (!watchedFiles.TryGetValue(file.FullName, out versioned))
{
lock (@lock)
{
versioned = watchedFiles[file.FullName] = new VersionedFileInfo
{
FileName = file.FullName,
Hash = GetFileHash(file.FullName),
Watcher = GetWatcher(file.DirectoryName)
};
}
}
var builder = new UriBuilder();
var query = HttpUtility.ParseQueryString(builder.Query);
query.Add(versionParameter, versioned.Hash);
builder.Path = helper.Content(path);
builder.Query = query.ToString();
return builder.Uri.PathAndQuery;
}
private static FileSystemWatcher GetWatcher(string directoryName)
{
var watcher = default(FileSystemWatcher);
if (!watchers.TryGetValue(directoryName, out watcher))
{
lock (@lock)
{
watcher = watchers[directoryName] = new FileSystemWatcher(directoryName);
watcher.Changed += OnWatcherChanged;
watcher.EnableRaisingEvents = true;
watcher.IncludeSubdirectories = false;
}
}
return watcher;
}
private static string GetFileHash(string fileName)
{
using (var md5 = new MD5CryptoServiceProvider())
{
var modified = File.GetLastWriteTime(fileName);
var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(modified.ToString()));
return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
}
}
private static void OnWatcherChanged(object sender, FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Changed)
{
if (watchedFiles.ContainsKey(e.FullPath))
{
var versioned = watchedFiles[e.FullPath];
versioned.Hash = GetFileHash(e.FullPath);
}
}
}
private class VersionedFileInfo
{
public string FileName;
public string Hash;
public FileSystemWatcher Watcher;
}
private static readonly IDictionary<string, FileSystemWatcher> watchers =
new Dictionary<string, FileSystemWatcher>();
private static readonly IDictionary<string, VersionedFileInfo> watchedFiles =
new Dictionary<string, VersionedFileInfo>();
private static readonly object @lock = new object();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment