Back to "Lite hacka: CSS Combiner / Minifier ASP.NET Control"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Imported mostlylucidcouk

Lite hacka: CSS Combiner / Minifier ASP.NET Control

Thursday, 25 March 2010

CAUTION: När jag säger hacka jag menar det ... detta är i NEJ sätt produktion redo ... det är bara ett prov i detta skede. Om det orsakar din hund att explodera, inte klandra mig!

Jag trodde att jag skulle kasta ett arbete pågår på nätet för människor att ha en lek med. Detta är en av mina små husdjur projekt (Jag får inte skriva kod på jobbet ... inte en dev som jag[Jag har alltid påmint!).

Så, vad gör den?

Tja, en av de viktigaste prestandaproblemen för nuvarande webbplatser är antalet förfrågningar de gör tillbaka till servern, inte den faktiska serversidan behandlingstid. När jag först skrev om detta efter att ha läst Steve Sounders utmärkt "Webbplatser med hög prestanda Det var uppriktigt sagt en överraskning för mig! Tja, i alla fall nyligen jag har spelat med ett antal ASP.NET-serverkontroll som syftar till att optimera antalet serversamtal en ASP.NET-app måste göra mycket färre. Den första av dessa är (ännu) opublicerade CSS Sprite Control (CSS Sprite Control). Nedan är början av den andra; Detta skrevs i ett par timmar igår kväll som jag kunde inte sova för att tänka på hur det kan fungera. Koden är mycket grov men det visar konceptet ganska bra.

I grund och botten ville jag ha minsta möjliga ansträngning för att kombinera och komprimera flera <länk rel= och stilmall> CSS-filposter tillsammans, minimera dem med hjälp av YUI-kompressor (för .NET ... på Codeplex) och mata ut en tidsberoende (i detta fall, Date Modified for the file) hash för filnamnet (på en fast plats för tillfället, till~/OutputCSS/ på), och sedan mata ut en tagg på sidan för den här nya filen.

Så, hur fungerar det?

Tänk dig att du hade en sida med några stilmallsdefinitioner som denna:

   1: <link type="text/css" rel="stylesheet" href="../../../build/logger/assets/logger.css"/>
   2: <link type="text/css" rel="stylesheet" href="../../../build/yuitest/assets/testlogger.css"/>        
   3: <style type="text/css">
   4: #container, #container2 {
   5:     width: 400px;
   6: }
   7:  
   8: .yui-carousel-element {
   9:     margin: 0;
  10:     padding: 0;
  11: }
  12:  
  13: .yui-carousel-element li {
  14:     border: none;
  15:     margin: 0;
  16:     padding: 0;
  17:     width: 100px;
  18: }
  19: </style>

Som ni kan se har vi två CSS fildefinitioner och en stil tag. Skulle det inte vara trevligt om vi kunde kombinera och minify dessa ... ja ... här är hur min lilla hacka kontroll låter dig göra det:

   1: <asp:CSSManager runat="server">
   2:         <link type="text/css" rel="stylesheet" href="~/build/logger/assets/logger.css"/>
   3:         <link type="text/css" rel="stylesheet" href="../../../build/yuitest/assets/testlogger.css"/>        
   4:         <style type="text/css">
   5:         #container, #container2 {
   6:             width: 400px;
   7:         }
   8:  
   9:         .yui-carousel-element {
  10:             margin: 0;
  11:             padding: 0;
  12:         }
  13:  
  14:         .yui-carousel-element li {
  15:             border: none;
  16:             margin: 0;
  17:             padding: 0;
  18:             width: 100px;
  19:         }
  20:         </style>
  21: </asp:CSSManager>

Du kan se i sippret ovan att vi precis lindade CSS definitioner i <Asp:CSSManager>…</asp:CSSManager> taggar...det är allt som finns till det! En annan sak, observanten kommer att ha märkt att jag nu kan använda en ~ / i href ... det är en trevlig bieffekt av denna metod ... låter dig undvika galna vägen traversala saker (.. /.. /. /. /)

Uppenbarligen är detta fortfarande lite begränsad, det fungerar inte i Partial Trust, inte hantera flera räckvidder; säg, Site nivå CSS, Page Level CSS och Masterpages där du vill ha flera ark med olika innehåll. Plan är att fortsätta arbeta på detta för en liten stund... så jag kommer nästan säkert uppdatera detta snart.

Källan följer

   1: using System;
   2: using System.Collections.Generic;
   3: using System.ComponentModel;
   4: using System.Text;
   5: using System.Web.UI;
   6: using System.Web.UI.HtmlControls;
   7: using System.IO;
   8: using System.Security.Cryptography;
   9: using Yahoo.Yui.Compressor;
  10:  
  11: namespace CSSManager
  12: {
  13:     [DefaultProperty("Text")]
  14:     [ToolboxData("<{0}:CSSManager runat=server></{0}:CSSManager>")]
  15:  
  16:     [ParseChildren(true, "cssFiles")]
  17:     public class CSSManager : Control, INamingContainer
  18:     {
  19:  
  20:         public const string FILE_PATH = "~/OutputCSS/{0}.css";
  21:  
  22:         public CSSManager() { }
  23:  
  24:         public List<HtmlGenericControl> CSSFiles
  25:         {
  26:             get;
  27:             set;
  28:         }
  29:  
  30:  
  31:         private StringBuilder combinedSheets = new StringBuilder();
  32:  
  33:         public string GetStyleSheet(string virtualPath)
  34:         {
  35:             string filePath = Context.Server.MapPath(virtualPath);
  36:             if (!string.IsNullOrEmpty(filePath))
  37:             {
  38:                 return File.ReadAllText(filePath);
  39:             }
  40:             return string.Empty;
  41:  
  42:         }
  43:  
  44:         public string CalculateFileHash(string filePaths)
  45:         {
  46:             MD5CryptoServiceProvider csp = new MD5CryptoServiceProvider();
  47:             byte[] pathBytes = csp.ComputeHash(System.Text.UTF8Encoding.UTF8.GetBytes(filePaths));
  48:             return BitConverter.ToUInt64(pathBytes, 0).ToString();
  49:  
  50:         }
  51:  
  52:         public void WriteFile(string textToWrite, string fileHash)
  53:         {
  54:             File.WriteAllText(Context.Server.MapPath(string.Format(FILE_PATH, fileHash)), textToWrite);
  55:         }
  56:  
  57:         public string CreateDatedFilePath(string filePath)
  58:         {
  59:             FileInfo fi = new FileInfo(Context.Server.MapPath(filePath));
  60:             if (fi.Exists)
  61:             {
  62:                 return fi.LastWriteTimeUtc.Ticks.ToString() + "#" + filePath;
  63:             }
  64:             return string.Empty;
  65:  
  66:  
  67:         }
  68:  
  69:         protected override void CreateChildControls()
  70:         {
  71:  
  72:             if (this.DesignMode == true)
  73:             {
  74:                 foreach (var file in CSSFiles)
  75:                 {
  76:                     string rel = file.Attributes["rel"];
  77:  
  78:                     string href = file.Attributes["href"];
  79:  
  80:                     if (rel.ToLowerInvariant() == "stylesheet")
  81:                     {
  82:                         file.Attributes["href"] = this.ResolveClientUrl(Context.Server.MapPath(href));
  83:                         this.Controls.Add(file);
  84:                     }
  85:  
  86:                 }
  87:             }
  88:             StringBuilder filePaths = new StringBuilder();
  89:             int i = 0;
  90:             foreach (var file in CSSFiles)
  91:             {
  92:                 string tagName = file.TagName.ToLowerInvariant();
  93:                 if (tagName == "link")
  94:                 {
  95:                     string rel = file.Attributes["rel"];
  96:                     string href = file.Attributes["href"];
  97:  
  98:                     if (rel.ToLowerInvariant() == "stylesheet")
  99:                     {
 100:                         string styleSheet = GetStyleSheet(href);
 101:                         if (!string.IsNullOrEmpty(styleSheet))
 102:                             combinedSheets.Append(styleSheet);
 103:                         filePaths.AppendFormat("{0}#", CreateDatedFilePath(href));
 104:                     }
 105:                 }
 106:                 else if (tagName == "style")
 107:                 {
 108:                     string inner = file.InnerHtml;
 109:                     combinedSheets.Append(inner);
 110:                     filePaths.Append("CSSSTYLE_" + i);
 111:                     i++;
 112:                 }
 113:  
 114:             }
 115:             string minimizedStyles = CssCompressor.Compress(combinedSheets.ToString(), 0, CssCompressionType.Hybrid);
 116:             string fileHash = CalculateFileHash(filePaths.ToString());
 117:             WriteFile(minimizedStyles, fileHash);
 118:  
 119:             HtmlGenericControl gen = new HtmlGenericControl();
 120:             gen.TagName = "link";
 121:             gen.Attributes.Add("href", this.ResolveClientUrl(string.Format(FILE_PATH, fileHash)));
 122:             gen.Attributes.Add("rel", "stylesheet");
 123:             this.Controls.Add(gen);
 124:         }
 125:  
 126:     }
 127: }
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.