Last active
August 29, 2015 14:22
-
-
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.)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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