📜 ⬆️ ⬇️

HttpHandler for compression and compression of * .js and * .css files

Everyone knows that “most of the time is spent on downloading the components of the page: pictures, style sheets, scripts, flash ... Reducing the number of these components reduces the number of requests to the server needed before the client application can render the page.” I always squeezed and merged * .js and * .css files manually, but lately it began to get me a bit, and I decided to simplify this process. To do this, I rummaged through a bunch of things on Google and thematic forums in search of the information I needed, and then I just put everything together.
For javascript compression, I used jscompress , slightly modified for my needs.
Here's what happened:

<%@ WebHandler Language= "C#" Class= "StaticFilesCombiner" %>

using System;
using System.Net;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Web;
using JSCompress;

public class StaticFilesCombiner: IHttpHandler {
private readonly static TimeSpan CACHE_DURATION = TimeSpan .FromDays(30);
private const string JQuery = "jquery-1.2.6.min.js" ;

public void ProcessRequest ( HttpContext context) {

HttpRequest request = context.Request;

// , contentType .
//
string setName = request[ "s" ] ?? string .Empty;
string contentType = request[ "t" ] ?? string .Empty;
string version = request[ "v" ] ?? string .Empty;

bool isCompressed = CanGZip(context.Request);
bool isJS = contentType.Contains( "java" );
UTF8Encoding encoding = new UTF8Encoding( false );

if (!WriteFromCache(context, setName, version, isCompressed, contentType))
{
using (MemoryStream memoryStream = new MemoryStream(5000))
{
using ( Stream writer = isCompressed ?
( Stream )( new GZipStream(memoryStream, CompressionMode.Compress)) :
memoryStream)
{

// <appSettings>
string setDefinition =
System.Configuration. ConfigurationManager .AppSettings[setName] ?? "" ;
string [] fileNames = setDefinition.Split( new [] { ',' },
StringSplitOptions.RemoveEmptyEntries);

foreach ( string fileName in fileNames)
{
byte [] fileBytes = GetFileBytes(context, fileName.Trim(), encoding, isJS);
writer.Write(fileBytes, 0, fileBytes.Length);
}

writer.Close();
}

byte [] responseBytes = memoryStream.ToArray();
context.Cache.Insert(GetCacheKey(setName, version, isCompressed),
responseBytes, null , System.Web.Caching.Cache.NoAbsoluteExpiration,
CACHE_DURATION);

WriteBytes(responseBytes, context, isCompressed, contentType);
}
}
}

/// <summary>
/// CSS.
/// </summary>
public static string MinifyCss( string body)
{
StringBuilder builder = new StringBuilder (body);
builder = builder.Replace( " " , string .Empty);
builder = builder.Replace(Environment.NewLine, string .Empty);
builder = builder.Replace( "\t" , string .Empty);
builder = builder.Replace( " {" , "{" );
builder = builder.Replace( " :" , ":" );
builder = builder.Replace( ": " , ":" );
builder = builder.Replace( ", " , "," );
builder = builder.Replace( "; " , ";" );
builder = builder.Replace( ";}" , "}" );

return builder.ToString();
}

/// <summary>
/// js.
/// </summary>
private static string MinifyJS( string notCompressedString, string virtualPath)
{
// JQuery, .
if (virtualPath.Contains(JQuery))
{
return notCompressedString;
}
JSCompressor jsCOmpressor = new JSCompressor( true )
{
CompressVariableNames = false ,
LineFeedRemoval = true
};
return jsCOmpressor.Compress(notCompressedString);
}

private static byte [] GetFileBytes( HttpContext context, string virtualPath, Encoding encoding, bool isJS)
{
string compressedString;
string notCompressedString;
if (virtualPath.StartsWith( "http://" , StringComparison.InvariantCultureIgnoreCase))
{
using (WebClient client = new WebClient())
{
notCompressedString = client.DownloadString(virtualPath);
compressedString = isJS ? MinifyJS(notCompressedString, virtualPath) : MinifyCss(notCompressedString);

return encoding.GetBytes(compressedString);
}
}

string physicalPath = context.Server.MapPath(virtualPath);
notCompressedString = File .ReadAllText(physicalPath);
compressedString = isJS ? MinifyJS(notCompressedString, physicalPath) : MinifyCss(notCompressedString);

return encoding.GetBytes(compressedString);
}

private static bool WriteFromCache( HttpContext context, string setName, string version,
bool isCompressed, string contentType)
{
byte [] responseBytes = context.Cache[GetCacheKey(setName, version, isCompressed)] as byte [];

if ( null == responseBytes || 0 == responseBytes.Length) return false ;

WriteBytes(responseBytes, context, isCompressed, contentType);
return true ;
}

private static void WriteBytes( byte [] bytes, HttpContext context,
bool isCompressed, string contentType)
{
HttpResponse response = context.Response;

response.AppendHeader( "Content-Length" , bytes.Length.ToString());
response.ContentType = contentType;
if (isCompressed)
response.AppendHeader( "Content-Encoding" , "gzip" );

context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetExpires( DateTime .Now.Add(CACHE_DURATION));
context.Response.Cache.SetMaxAge(CACHE_DURATION);
context.Response.Cache.AppendCacheExtension( "must-revalidate, proxy-revalidate" );

response.OutputStream.Write(bytes, 0, bytes.Length);
response.Flush();
}

private static bool CanGZip(HttpRequest request)
{
string acceptEncoding = request.Headers[ "Accept-Encoding" ];
if (! string .IsNullOrEmpty(acceptEncoding) &&
(acceptEncoding.Contains( "gzip" ) || acceptEncoding.Contains( "deflate" )))
return true ;
return false ;
}

private static string GetCacheKey( string setName, string version, bool isCompressed)
{
return "StaticFilesCombiner." + setName + "." + version + "." + isCompressed;
}

public bool IsReusable
{
get
{
return true ;
}
}

}


* This source code was highlighted with Source Code Highlighter .


To use it, just add a couple of lines to web.config:

< appSettings >
< add key ="Main_Css" value ="~/styles/styles.css" />
< add key ="Set_Css" value ="~/styles/Compare.css,~/styles/anycss.css,~/styles/ImageGallery.css,~/styles/styles.css,~/styles/thickbox.css" />
< add key ="Set_Javascript" value ="~/js/jquery-1.2.6.min.js,~/js/basket.js,~/js/jquery.galleria.js,~/js/thickbox-compressed.js" />
< add key ="SiteName" value ="stirka.spb.ru" />
</ appSettings >


* This source code was highlighted with Source Code Highlighter .

')
In fact, these are sets of files for combining and compressing.

And the use itself:

< link type ="text/css" rel ="Stylesheet" href ="StaticFilesCombiner.ashx?s=Main_Css&t=text/css&v=13" />

* This source code was highlighted with Source Code Highlighter .

Source: https://habr.com/ru/post/44036/


All Articles