Skip to content

Instantly share code, notes, and snippets.

@dieseltravis
Created June 16, 2009 21:32
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dieseltravis/130913 to your computer and use it in GitHub Desktop.
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
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
<%@ WebHandler Language="C#" CodeBehind="YuiCompressor.ashx.cs" Class="YuiCompressor" %>
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]);
}
}
}
'<%@ 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
@replete
Copy link

replete commented Nov 13, 2012

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment