Created
June 16, 2009 21:32
-
-
Save dieseltravis/130913 to your computer and use it in GitHub Desktop.
A C# ASP.Net handler that concatenates, compresses, and caches CSS/JS files
This file contains hidden or 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
Example usage: | |
...in <head>: | |
<link rel="stylesheet" type="text/css" href="/YuiCompressor.ashx?css=reset,style" /> | |
...just before the </body>: | |
<script type="text/javascript" src="/YuiCompressor.ashx?js=main,someotherscript"></script> | |
Changelog: | |
2010-02-18: Updated to support HTTP compression |
This file contains hidden or 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
<%@ WebHandler Language="C#" CodeBehind="YuiCompressor.ashx.cs" Class="YuiCompressor" %> |
This file contains hidden or 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.Web; | |
using Yahoo.Yui.Compressor; | |
public class YuiCompressor : IHttpHandler | |
{ | |
public const char DELIMITER = ','; | |
public const bool ENABLEHTTPCOMPRESSION = true; | |
public TimeSpan CacheDuration = TimeSpan.FromDays(1); | |
public class ContentTypes | |
{ | |
public const string CSS = "text/css"; | |
public const string JS = "application/x-javascript"; | |
public const string ERROR = "text/plain"; | |
} | |
public class Folders | |
{ | |
// customize these to your site's paths | |
public const string CSS = "~/styles/"; | |
public const string JS = "~/scripts/"; | |
} | |
public class Extensions | |
{ | |
public const string CSS = ".css"; | |
public const string JS = ".js"; | |
} | |
public void ProcessRequest(HttpContext context) | |
{ | |
context.Response.ContentEncoding = System.Text.Encoding.Default; | |
context.Response.Cache.SetExpires(DateTime.Now.Add(CacheDuration)); | |
context.Response.Cache.SetMaxAge(CacheDuration); | |
context.Response.Cache.SetCacheability(HttpCacheability.Private); | |
string cssFiles = context.Request.QueryString["css"]; | |
string jsFiles = context.Request.QueryString["js"]; | |
string fileName = context.Request.QueryString["f"]; | |
if (! string.IsNullOrEmpty(cssFiles)) | |
{ | |
// A list of CSS files has been passed in, write each file's contents to the response object | |
context.Response.ContentType = ContentTypes.CSS; | |
foreach (string cssFile in cssFiles.Split(DELIMITER)) | |
{ | |
WriteCompressedFile(context, cssFile + Extensions.CSS, Folders.CSS); | |
} | |
} | |
else if (! string.IsNullOrEmpty(jsFiles)) | |
{ | |
// A list of JS files has been passed in, write each file's contents to the response object | |
context.Response.ContentType = ContentTypes.JS; | |
foreach (string jsFile in jsFiles.Split(DELIMITER)) | |
{ | |
WriteCompressedFile(context, jsFile + Extensions.JS, Folders.JS); | |
} | |
} | |
else if (! string.IsNullOrEmpty(fileName)) | |
{ | |
// A specific file has been passed in, write that file's contents to the response object | |
if (fileName.EndsWith(Extensions.JS)) | |
{ | |
context.Response.ContentType = ContentTypes.JS; | |
WriteCompressedFile(context, fileName, Folders.JS); | |
} | |
else if (fileName.EndsWith(Extensions.CSS)) | |
{ | |
context.Response.ContentType = ContentTypes.CSS; | |
WriteCompressedFile(context, fileName, Folders.CSS); | |
} | |
else | |
{ | |
// 500? | |
//Throw New System.IO.FileNotFoundException("The file specified isn't an allowed type.", fileName) | |
context.Response.ContentType = ContentTypes.ERROR; | |
} | |
} | |
else | |
{ | |
// 404? | |
//Throw New System.IO.FileNotFoundException("A filename hasn't been specified.") | |
context.Response.ContentType = ContentTypes.ERROR; | |
context.Response.Write(Environment.NewLine); | |
} | |
// compress output if enabled | |
if (ENABLEHTTPCOMPRESSION) | |
{ | |
string encodingsAccepted = context.Request.Headers("Accept-Encoding"); | |
if (! string.IsNullOrEmpty(encodingsAccepted)) | |
{ | |
encodingsAccepted = encodingsAccepted.ToLowerInvariant(); | |
if (encodingsAccepted.Contains("deflate")) | |
{ | |
context.Response.AppendHeader("Content-Encoding", "deflate"); | |
context.Response.Filter = New System.IO.Compression.DeflateStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress); | |
} | |
else if (encodingsAccepted.Contains("gzip")) | |
{ | |
context.Response.AppendHeader("Content-Encoding", "gzip"); | |
context.Response.Filter = New System.IO.Compression.GZipStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress); | |
} | |
} | |
} | |
} | |
public bool IsReusable | |
{ | |
get | |
{ | |
return false; | |
} | |
} | |
public void WriteCompressedFile(HttpContext context, string fileName, string folder) | |
{ | |
// Add each file's compressed contents to the cache as it is read with a file dependency on itself | |
if (context.Cache[fileName] == null) | |
{ | |
string filePath = HttpContext.Current.Server.MapPath(folder + fileName); | |
string output = string.Empty; | |
try | |
{ | |
output = System.IO.File.ReadAllText(filePath); | |
if (folder == Folders.JS) | |
{ | |
output = JavaScriptCompressor.Compress(output) + Environment.NewLine; | |
} | |
else | |
{ | |
output = CssCompressor.Compress(output) + Environment.NewLine; | |
} | |
context.Response.Write(output); | |
context.Cache.Insert(fileName, output, new System.Web.Caching.CacheDependency(filePath)); | |
} | |
catch (System.IO.FileNotFoundException) | |
{ | |
// throw 404? | |
} | |
} | |
else | |
{ | |
context.Response.Write((string) context.Cache[fileName]); | |
} | |
} | |
} |
This file contains hidden or 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
'<%@ WebHandler Language="VB" Class="YuiCompressor" %> | |
Imports System | |
Imports System.Web | |
Imports Yahoo.Yui.Compressor | |
Public Class YuiCompressor : Implements IHttpHandler | |
Public Const Delimiter As String = "," | |
Public Const EnableHttpCompression As Boolean = True | |
Public cacheDuration As TimeSpan = TimeSpan.FromDays(1) | |
Public Class ContentTypes | |
Public Const Css As String = "text/css" | |
Public Const Js As String = "application/x-javascript" | |
Public Const Err As String = "text/plain" | |
End Class | |
Public Class Folders | |
Public Const Css As String = "~/styles/" | |
Public Const Js As String = "~/scripts/" | |
End Class | |
Public Class Extensions | |
Public Const Css As String = ".css" | |
Public Const Js As String = ".js" | |
End Class | |
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest | |
context.Response.ContentEncoding = Encoding.Default | |
context.Response.Cache.SetExpires(DateTime.Now.Add(cacheDuration)) | |
context.Response.Cache.SetMaxAge(cacheDuration) | |
context.Response.Cache.SetCacheability(HttpCacheability.Private) | |
Dim cssFiles As String = context.Request.QueryString("css") | |
Dim jsFiles As String = context.Request.QueryString("js") | |
Dim fileName As String = context.Request.QueryString("f") | |
If Not String.IsNullOrEmpty(cssFiles) Then | |
' A list of CSS files has been passed in, write each file's contents to the response object | |
context.Response.ContentType = ContentTypes.Css | |
For Each cssFile As String In cssFiles.Split(Delimiter) | |
WriteCompressedFile(context, cssFile & Extensions.Css, Folders.Css) | |
Next | |
ElseIf Not String.IsNullOrEmpty(jsFiles) Then | |
' A list of JS files has been passed in, write each file's contents to the response object | |
context.Response.ContentType = ContentTypes.Js | |
For Each jsFile As String In jsFiles.Split(Delimiter) | |
WriteCompressedFile(context, jsFile & Extensions.Js, Folders.Js) | |
Next | |
ElseIf Not String.IsNullOrEmpty(fileName) Then | |
' A specific file has been passed in, write that file's contents to the response object | |
If fileName.EndsWith(Extensions.Js) Then | |
context.Response.ContentType = ContentTypes.Js | |
WriteCompressedFile(context, fileName, Folders.Js) | |
ElseIf fileName.EndsWith(Extensions.Css) Then | |
context.Response.ContentType = ContentTypes.Css | |
WriteCompressedFile(context, fileName, Folders.Css) | |
Else | |
' 500? | |
'Throw New System.IO.FileNotFoundException("The file specified isn't an allowed type.", fileName) | |
context.Response.ContentType = ContentTypes.Err | |
End If | |
Else | |
' 404? | |
'Throw New System.IO.FileNotFoundException("A filename hasn't been specified.") | |
context.Response.ContentType = ContentTypes.Err | |
context.Response.Write(Environment.NewLine) | |
End If | |
' compress output if enabled | |
If EnableHttpCompression Then | |
Dim encodingsAccepted As String = context.Request.Headers("Accept-Encoding") | |
If Not (String.IsNullOrEmpty(encodingsAccepted)) Then | |
encodingsAccepted = encodingsAccepted.ToLowerInvariant() | |
If (encodingsAccepted.Contains("deflate")) Then | |
context.Response.AppendHeader("Content-Encoding", "deflate") | |
context.Response.Filter = New System.IO.Compression.DeflateStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress) | |
ElseIf (encodingsAccepted.Contains("gzip")) Then | |
context.Response.AppendHeader("Content-Encoding", "gzip") | |
context.Response.Filter = New System.IO.Compression.GZipStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress) | |
End If | |
End If | |
End If | |
End Sub | |
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable | |
Get | |
Return False | |
End Get | |
End Property | |
Public Sub WriteCompressedFile(ByVal context As HttpContext, ByVal fileName As String, ByVal folder As String) | |
' Add each file's compressed contents to the cache as it is read with a file dependency on itself | |
Dim output As String = String.Empty | |
If context.Cache(fileName) Is Nothing Then | |
Dim filePath As String = HttpContext.Current.Server.MapPath(folder & fileName) | |
Try | |
output = System.IO.File.ReadAllText(filePath) | |
If folder = Folders.Js Then | |
output = JavaScriptCompressor.Compress(output) & vbCrLf | |
Else | |
output = CssCompressor.Compress(output) & vbCrLf | |
End If | |
context.Cache.Insert(fileName, output, New CacheDependency(filePath)) | |
Catch fnf As System.IO.FileNotFoundException | |
' throw 404? | |
End Try | |
Else | |
output = DirectCast(HttpContext.Current.Cache(fileName), String) | |
End If | |
context.Response.Write(output) | |
End Sub | |
End Class |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You had static references to non-static reference objects, this has now been updated in this gist: https://gist.github.com/4065050