Complete solution for Combine, Minify and GZIP of your styles and scripts on Asp.Net MVC3 using Razor

Sorry for my poor English, but I think this will not be a problem. I just don’t want to share the good helper class that I made for combining , minify and gzip our scripts and styles using Microsoft Ajax Minifier . Before starting, download ICSharpCode.SharpZipLib . This is an open source lib to use gzip .

Start with web.config (I will focus on IIS7). Here we tell our application that any request made in the cssh or jsh extension will be redirected to the MinifierHelper class. I decided to use these extensions ( cssh and jsh ) if we want, for some reason, not to minimize a specific script or style, use it the way you usually use.

<system.webServer> <handlers> <remove name="ScriptHandler" /> <remove name="StyleHandler" /> <add name="ScriptHandler" verb="*" path="*.jsh" type="Site.Helpers.MinifierHelper" resourceType="Unspecified" /> <add name="StyleHandler" verb="*" path="*.cssh" type="Site.Helpers.MinifierHelper" resourceType="Unspecified" /> </handlers> </system.webServer> 

I use the Scripts and Styles folders to store files. I do not use the contents of the folder as suggested by Visual Studio.

The next step is to configure global.asax. We must tell our application not to route these folders. Add these lines to your RegisterRoutes method .

 public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("Scripts/{*path}"); routes.IgnoreRoute("Styles/{*path}"); ... } 

OK Now I will show how to use our class. In view:

 <link href="/Styles/Folder/File.cssh" type="text/css" rel="stylesheet" /> <script src="/Scripts/Folder/File.jsh" type="text/javascript"></script> 

In my example, I made the logic for all my scripts and styles inside folders inside scripts / styles. Example: Site β†’ Scripts β†’ Home β†’ index.css . I use the same structure as for representations in scripts and styles. Example: Site β†’ Views β†’ Home β†’ index.cshtml . You can change this template if you want.

Now the code to create magic:

 using System; using System.Collections.Generic; using System.Configuration; using System.Globalization; using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Web; using System.Web.Caching; using System.Web.SessionState; using ICSharpCode.SharpZipLib.GZip; using Microsoft.Ajax.Utilities; namespace Site.Helpers { public abstract class MinifierBase : IHttpHandler, IRequiresSessionState { #region Fields private HttpContext context; private byte[] responseBytes; private bool isScript; protected string fileName; protected string folderName; protected List<string> files; #endregion #region Properties public bool IsReusable { get { return false; } } #endregion #region Methods public static string setUrl(string url) { var publishDate = ConfigurationManager.AppSettings["PublishDate"]; return url + "h" + ((publishDate != null) ? "?id=" + publishDate : ""); } public void ProcessRequest(HttpContext context) { this.context = context; this.isScript = context.Request.Url.PathAndQuery.Contains("/Scripts/"); this.process(); } private void process() { if (this.context.Request.QueryString.HasKeys()) { string url = this.context.Request.Url.PathAndQuery; if (this.context.Cache[url] != null) { this.responseBytes = this.context.Cache[url] as byte[]; } else { this.writeResponseBytes(); this.context.Cache.Add ( url, this.responseBytes, null, DateTime.Now.AddMonths(1), Cache.NoSlidingExpiration, CacheItemPriority.Low, null ); } } else { this.writeResponseBytes(); } this.writeBytes(); } private void writeResponseBytes() { using (MemoryStream ms = new MemoryStream(8092)) { using (Stream writer = this.canGZip() ? (Stream)(new GZipOutputStream(ms)) : ms) { var sb = new StringBuilder(); var regex = new Regex(@"^/.+/(?<folder>.+)/(?<name>.+)\..+"); var url = regex.Match(this.context.Request.Path); var folderName = url.Groups["folder"].Value; var fileName = url.Groups["name"].Value; this.getFileNames(fileName, folderName).ForEach(delegate(string file) { sb.Append(File.ReadAllText(this.context.Server.MapPath(file))); }); var minifier = new Minifier(); var minified = string.Empty; if (this.isScript) { var settings = new CodeSettings(); settings.LocalRenaming = LocalRenaming.CrunchAll; settings.OutputMode = OutputMode.SingleLine; settings.PreserveImportantComments = false; settings.TermSemicolons = true; minified = minifier.MinifyJavaScript(sb.ToString(), settings); } else { var settings = new CssSettings(); settings.CommentMode = CssComment.Important; settings.OutputMode = OutputMode.SingleLine; minified = minifier.MinifyStyleSheet(sb.ToString(), settings); } var bts = Encoding.UTF8.GetBytes(minified); writer.Write(bts, 0, bts.Length); } this.responseBytes = ms.ToArray(); } } private List<String> getFileNames(string fileName, string folderName = "") { this.files = new List<String>(); this.fileName = fileName; this.folderName = folderName; if (folderName == "Global" && fileName == "global-min") { if (this.isScript) this.addGlobalScripts(); else this.addDefaultStyles(); } else { var flags = BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance; var mi = this.GetType().GetMethod ( "add" + this.folderName + CultureInfo.CurrentCulture.TextInfo.ToTitleCase(fileName).Replace("-", "") + (this.isScript ? "Scripts" : "Styles"), flags ); if (mi != null) { mi.Invoke(this, null); } else { if (this.isScript) this.addDefaultScripts(); else this.addDefaultStyles(); } } return files; } private void writeBytes() { var response = this.context.Response; response.AppendHeader("Content-Length", this.responseBytes.Length.ToString()); response.ContentType = this.isScript ? "text/javascript" : "text/css"; if (this.canGZip()) { response.AppendHeader("Content-Encoding", "gzip"); } else { response.AppendHeader("Content-Encoding", "utf-8"); } response.ContentEncoding = Encoding.Unicode; response.OutputStream.Write(this.responseBytes, 0, this.responseBytes.Length); response.Flush(); } private bool canGZip() { string acceptEncoding = this.context.Request.Headers["Accept-Encoding"]; return (!string.IsNullOrEmpty(acceptEncoding) && (acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate"))); } protected abstract void addGlobalScripts(); protected abstract void addGlobalStyles(); protected abstract void addDefaultScripts(); protected abstract void addDefaultStyles(); #endregion } 

This is the base class. Now we will create the Helper class, which inherits from the base class. What is the class in which we choose which scripts should be added.

 public class MinifierHelper : MinifierBase { #region Methods 

To combine and minimize global scripts / styles, add the current line to your view:

 <link href="@MinifierHelper.setUrl("/Styles/Global/global-min.css")" type="text/css" rel="stylesheet" /> <script src="@MinifierHelper.setUrl("/Scripts/Global/global-min.js")" type="text/javascript"></script> 

It will call the addGlobalScripts / addGlobalStyles methods in our MinifierHelper class.

  protected override void addGlobalScripts() { this.files.Add("~/Scripts/Lib/jquery-1.6.2.js"); this.files.Add("~/Scripts/Lib/jquery-ui-1.8.16.js"); this.files.Add("~/Scripts/Lib/jquery.unobtrusive-ajax.js"); this.files.Add("~/Scripts/Lib/jquery.validate.js"); ... } protected override void addGlobalStyles() { this.files.Add("~/Styles/Global/reset.css"); this.files.Add("~/Styles/Global/main.css"); this.files.Add("~/Styles/Global/form.css"); ... } 

To minimize the specific script / style (page specific), add the current line to your view:

 <link href="@MinifierHelper.setUrl("/Styles/Curriculum/index.css")" type="text/css" rel="stylesheet" /> 

The MinifierHelper class will try to find a method named "add" + FolderName + FileName + "Styles" . In our case, it will look for addCurriculumIndexStyles . In my example, it exists, so the method will be launched.

  public void addCurriculumIndexStyles() { this.files.Add("~/Styles/Global/curriculum.css"); this.files.Add("~/Styles/Curriculum/personal-info.css"); this.files.Add("~/Styles/Curriculum/academic-info.css"); this.files.Add("~/Styles/Curriculum/professional-info.css"); } 

If the class does not find this particular method, it will call the default method. The default method minimizes the script / style using the same folder / name.

 protected override void addDefaultScripts() { this.files.Add("~/Scripts/" + this.folderName + "/" + this.fileName + ".js"); } protected override void addDefaultStyles() { this.files.Add("~/Styles/" + this.folderName + "/" + this.fileName + ".css"); } 

Remember to close the scope and class.

  #endregion } 

Here it is. I hope you guys understand.

I forgot to say the last. In web.config, add the key to AppSettings named PublishDate. I entered a string with the full date and time (for example: 261020111245). The goal is to be unique. This key will be used to cache our mini scripts. If you do not create this key, your application will not use the cache. I recommend using this. Therefore, every time you update your scripts / styles, also update your PublishDate.

 <add key="PublishDate" value="261020111245" /> 
+7
source share
1 answer

The Mindscape Web Workbench is a great tool for completing the items you are looking for.

http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a

Here is a good blog post:

http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a

+1
source

All Articles