public
Last active

A C# ASP.Net handler that concatenates, compresses, and caches CSS/JS files

  • Download Gist
Readme.txt
1 2 3 4 5 6 7 8 9
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
YuiCompressor.ashx
ASP
1
<%@ WebHandler Language="C#" CodeBehind="YuiCompressor.ashx.cs" Class="YuiCompressor" %>
YuiCompressor.ashx.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
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]);
}
}
}
YuiCompressor.ashx.vb
Visual Basic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
'<%@ 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

You had static references to non-static reference objects, this has now been updated in this gist: https://gist.github.com/4065050

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.