# Pequeño hack: CSS Combiner / Minifier ASP.NET Control

<datetime class="hidden">2010-03-25T00:00</datetime>

<!-- category -- mostlylucidcouk, Imported -->
**PRECAUCIÓN: Cuando digo hackear lo digo en serio... esto está en NO MANERA la producción lista... es sólo una muestra en esta etapa. Si hace que su perro explote, no me culpes!**

Pensé que iba a lanzar un trabajo en progreso en línea para que la gente tenga un juego con. Este es uno de mis pequeños proyectos de mascotas (No se me permite escribir código en el trabajo ... no un dev como I '[m siempre recordado!).

Entonces, ¿qué hace?

Bueno, uno de los principales problemas de rendimiento para los sitios web actuales es el número de solicitudes que hacen de vuelta al servidor, no el tiempo real de procesamiento del lado del servidor. [escribió sobre esto](http://mostlylucid.net/archive/2008/07/11/spritey.my-pet-project.aspx) después de leer Steve Sounders excelente ‘[Sitios web de alto rendimiento](http://www.amazon.com/High-Performance-Web-Sites-Essential) Fue francamente una sorpresa para mí! Bueno, de todos modos recientemente he estado jugando con un número de ASP.NET control de servidor que tienen como objetivo optimizar el número de llamadas de servidor una aplicación ASP.NET tiene que hacer mucho menos. El primero de estos es el (hasta ahora) inédito [Control de sprite CSS](http://mostlylucid.net/archive/2008/07/11/spritey.my-pet-project.aspx). A continuación está el comienzo de la segunda; esto fue escrito en un par de horas anoche, ya que no podía dormir para pensar en cómo podría funcionar. El código es MUY áspero, pero demuestra el concepto bastante bien.

En esencia quería el mínimo esfuerzo posible para combinar y comprimir múltiples &lt;link rel=”stylesheet”&gt; Las entradas de archivos CSS juntas, minimizarlas utilizando [Compresor YUI](http://developer.yahoo.com/yui/compressor/) ([para .NET...en Codeplex](http://yuicompressor.codeplex.com/)) y salida un tiempo dependiente (en este caso, Fecha Modificado para el archivo) hash para el nombre del archivo (en una ubicación fija en el momento, “~/OutputCSS/”), a continuación, salida de una etiqueta en la página para este nuevo archivo.

Entonces, ¿cómo funciona?

Bueno, imagínate que tenías una página con algunas definiciones de hoja de estilo como esta:

```
   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>
```

Como se puede ver tenemos dos definiciones de archivos CSS y una etiqueta de estilo. ¿No sería bueno si pudiéramos combinar y minificar estos ... bueno ... así es como mi pequeño control hacky te permite hacerlo:

```
   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>
```

Se puede ver en el fragmento de arriba que acabamos de envolver las definiciones CSS en &lt;asp:CSSManager&gt;…&lt;/asp:CSSManager&gt; etiquetas ... eso es todo lo que hay a él! Una cosa más, el observador se habrá dado cuenta de que ahora puedo utilizar un ~/ en el href ... es un buen efecto secundario de este método ... permite evitar el camino loco cosas transversales (../../././)

Obviamente esto es todavía un poco limitado, no funciona en Parcial Trust, no maneja múltiples visores; digamos, Site level CSS, Page Level CSS y Masterpages donde usted querría múltiples hojas con diferentes contenidos. Plan es seguir trabajando en esto por un poco de tiempo... así que casi seguro que actualizaré esto pronto.

La fuente sigue

```
   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: }
```