【转】HttpCompress


在PreRequestHandlerExecute 事件里边用DeflateStream修饰的Response.Filter替代Response.Filter
  public sealed class CompressionModule : IHttpModule
  {

    #region IHttpModule Members

    /// <summary>
    /// Disposes of the resources (other than memory) used by the module
    /// that implements <see cref="T:System.Web.IHttpModule"></see>.
    /// </summary>
    void IHttpModule.Dispose()
    {
      // Nothing to dispose;
    }

    /// <summary>
    /// Initializes a module and prepares it to handle requests.
    /// </summary>
    /// <param name="context">An <see cref="T:System.Web.HttpApplication"></see>
    /// that provides access to the methods, properties, and events common to
    /// all application objects within an ASP.NET application.
    /// </param>
    void IHttpModule.Init(HttpApplication context)
    {
      if (BlogSettings.Instance.EnableHttpCompression)
      {
        context.PreRequestHandlerExecute += new EventHandler(context_PostReleaseRequestState);
      }
    }

    #endregion

    private const string GZIP = "gzip";
    private const string DEFLATE = "deflate";

    #region Compress page

    /// <summary>
    /// Handles the BeginRequest event of the context control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    void context_PostReleaseRequestState(object sender, EventArgs e)
    {
      HttpApplication app = (HttpApplication)sender;
      if (app.Context.CurrentHandler is System.Web.UI.Page && app.Request["HTTP_X_MICROSOFTAJAX"] == null)
      {
        if (IsEncodingAccepted(DEFLATE))
        {
          app.Response.Filter = new DeflateStream(app.Response.Filter, CompressionMode.Compress);
          SetEncoding(DEFLATE);
        }
        else if (IsEncodingAccepted(GZIP))
        {
          app.Response.Filter = new GZipStream(app.Response.Filter, CompressionMode.Compress);
          SetEncoding(GZIP);
        }
      }
            else if (app.Context.Request.Path.Contains("WebResource.axd"))
            {
                app.Context.Response.Cache.SetExpires(DateTime.Now.AddDays(30));
            }
    }

    /// <summary>
    /// Checks the request headers to see if the specified
    /// encoding is accepted by the client.
    /// </summary>
    private static bool IsEncodingAccepted(string encoding)
    {
      HttpContext context = HttpContext.Current;
      return context.Request.Headers["Accept-encoding"] != null && context.Request.Headers["Accept-encoding"].Contains(encoding);
    }   

    /// <summary>
    /// Adds the specified encoding to the response headers.
    /// </summary>
    /// <param name="encoding"></param>
    private static void SetEncoding(string encoding)
    {
      HttpContext.Current.Response.AppendHeader("Content-encoding", encoding);
    }

    #endregion

  }

二:
HttpCompress是在.NET内部通过httpModules模型来实现自定义的压缩模块。

1. 配置
<httpModules>
      <add name="CompressionModule" type="blowery.Web.HttpCompress.HttpModule, blowery.web.HttpCompress"/>
</httpModules> 

2.注册事件钩子
context.ReleaseRequestState += new EventHandler(this.CompressContent);
context.PreSendRequestHeaders += new EventHandler(this.CompressContent);
IHttpModule事件

3.在代码发送前压缩

void CompressContent(object sender, EventArgs e) {
HttpApplication app 
= (HttpApplication)sender;
      
if(!app.Context.Items.Contains(INSTALLED_KEY)) {
//设置标志避免重复压缩
        app.Context.Items.Add(INSTALLED_KEY, INSTALLED_TAG);

//设置缓存敏感参数
       app.Response.Cache.VaryByHeaders["Accept-Encoding"= true;

//根据Accept-Encoding寻找合适的压缩过滤模块CompressingFilter : HttpOutputFilter;
//如果没有设置Accept-Encoding,或者寻找失败均是直接返回,即不压缩
...
CompressingFilter filter 
= GetFilterForScheme(types, app.Response.Filter, settings);
        
if(filter == null){
          
// if we didn't find a filter, bail out
          return;
        }

        app.Response.Filter 
= filter;
}

}

4.压缩类实现

HttpOutputFilter ,不支持Read, Seek,GetLength等操作,与之相关控制属性返回false, 具体方法或属性则抛异常new NotSupportedException(); 

using System;
using System.IO;

namespace blowery.Web.HttpCompress {
  
/// <summary>
  
/// The base of anything you want to latch onto the Filter property of a <see cref="System.Web.HttpResponse"/>
  
/// object.
  
/// </summary>
  
/// <remarks>
  
/// <p></p>These are generally used with <see cref="HttpModule"/> but you could really use them in
  
/// other HttpModules.  This is a general, write-only stream that writes to some underlying stream.  When implementing
  
/// a real class, you have to override void Write(byte[], int offset, int count).  Your work will be performed there.
  
/// </remarks>

  public abstract class HttpOutputFilter : Stream {
    
    
private Stream _sink;

    
/// <summary>
    
/// Subclasses need to call this on contruction to setup the underlying stream
    
/// </summary>
    
/// <param name="baseStream">The stream we're wrapping up in a filter</param>

    protected HttpOutputFilter(Stream baseStream) 
      _sink 
= baseStream;
    }


    
/// <summary>
    
/// Allow subclasses access to the underlying stream
    
/// </summary>

    protected Stream BaseStream {
      
getreturn _sink; }
    }


    
/// <summary>
    
/// False.  These are write-only streams
    
/// </summary>

    public override bool CanRead {
      
get return false; }
    }


    
/// <summary>
    
/// False.  These are write-only streams
    
/// </summary>

    public override bool CanSeek {
      
get return false; }
    }


    
/// <summary>
    
/// True.  You can write to the stream.  May change if you call Close or Dispose
    
/// </summary>

    public override bool CanWrite {
      
get return _sink.CanWrite; }
    }


    
/// <summary>
    
/// Not supported.  Throws an exception saying so.
    
/// </summary>
    
/// <exception cref="NotSupportedException">Thrown.  Always.</exception>

    public override long Length {
      
get throw new NotSupportedException(); }
    }


    
/// <summary>
    
/// Not supported.  Throws an exception saying so.
    
/// </summary>
    
/// <exception cref="NotSupportedException">Thrown.  Always.</exception>

    public override long Position {
      
get throw new NotSupportedException(); }
      
set throw new NotSupportedException(); }
    }


    
/// <summary>
    
/// Not supported.  Throws an exception saying so.
    
/// </summary>
    
/// <exception cref="NotSupportedException">Thrown.  Always.</exception>

    public override long Seek(long offset, System.IO.SeekOrigin direction) {
      
throw new NotSupportedException();
    }


    
/// <summary>
    
/// Not supported.  Throws an exception saying so.
    
/// </summary>
    
/// <exception cref="NotSupportedException">Thrown.  Always.</exception>

    public override void SetLength(long length) {
      
throw new NotSupportedException();
    }

    
    
/// <summary>
    
/// Closes this Filter and the underlying stream.
    
/// </summary>
    
/// <remarks>
    
/// If you override, call up to this method in your implementation.
    
/// </remarks>

    public override void Close() {
      _sink.Close();
    }


    
/// <summary>
    
/// Fluses this Filter and the underlying stream.
    
/// </summary>
    
/// <remarks>
    
/// If you override, call up to this method in your implementation.
    
/// </remarks>

    public override void Flush() {
      _sink.Flush();
    }


    
/// <summary>
    
/// Not supported.
    
/// </summary>
    
/// <param name="buffer">The buffer to write into.</param>
    
/// <param name="offset">The offset on the buffer to write into</param>
    
/// <param name="count">The number of bytes to write.  Must be less than buffer.Length</param>
    
/// <returns>An int telling you how many bytes were written</returns>

    public override int Read(byte[] buffer, int offset, int count) {
      
throw new NotSupportedException();
    }


  }

}

CompressingFilter 增加了压缩相关的属性和方法,如,写头

using System;
using System.IO;
using System.Web;

namespace blowery.Web.HttpCompress {
  
/// <summary>
  
/// Base for any HttpFilter that performing compression
  
/// </summary>
  
/// <remarks>
  
/// When implementing this class, you need to implement a <see cref="HttpOutputFilter"/>
  
/// along with a <see cref="CompressingFilter.ContentEncoding"/>.  The latter corresponds to a 
  
/// content coding (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
  
/// that your implementation will support.
  
/// </remarks>

  public abstract class CompressingFilter : HttpOutputFilter {

    
private bool hasWrittenHeaders = false;

    
/// <summary>
    
/// Protected constructor that sets up the underlying stream we're compressing into
    
/// </summary>
    
/// <param name="baseStream">The stream we're wrapping up</param>
    
/// <param name="compressionLevel">The level of compression to use when compressing the content</param>

    protected CompressingFilter(Stream baseStream, CompressionLevels compressionLevel) : base(baseStream) {
      _compressionLevel 
= compressionLevel;
    }


    
/// <summary>
    
/// The name of the content-encoding that's being implemented
    
/// </summary>
    
/// <remarks>
    
/// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5 for more
    
/// details on content codings.
    
/// </remarks>

    public abstract string ContentEncoding get; }

    
private CompressionLevels _compressionLevel;

    
/// <summary>
    
/// Allow inheriting classes to get access the the level of compression that should be used
    
/// </summary>

    protected CompressionLevels CompressionLevel {
      
get return _compressionLevel; }
    }


    
/// <summary>
    
/// Keeps track of whether or not we're written the compression headers
    
/// </summary>

    protected bool HasWrittenHeaders 
      
get return hasWrittenHeaders; } 
    }


    
/// <summary>
    
/// Writes out the compression-related headers.  Subclasses should call this once before writing to the output stream.
    
/// </summary>

    protected void WriteHeaders() {
      
// this is dangerous.  if Response.End is called before the filter is used, directly or indirectly,
      
// the content will not pass through the filter.  However, this header will still be appended.  
      
// Look for handling cases in PreRequestSendHeaders and Pre
      HttpContext.Current.Response.AppendHeader("Content-Encoding"this.ContentEncoding);
      
//HttpContext.Current.Response.AppendHeader("X-Compressed-By", "HttpCompress");
      hasWrittenHeaders = true;
    }



  }

}

执行类,利用压缩类实现了Write,Close,Flush

using System;
using System.IO;

using System.IO.Compression;

namespace blowery.Web.HttpCompress {
  
/// <summary>
  
/// Summary description for DeflateFilter.
  
/// </summary>

  public class DeflateFilter : CompressingFilter {

    
/// <summary>
    
/// compression stream member
    
/// has to be a member as we can only have one instance of the
    
/// actual filter class
    
/// </summary>

    private DeflateStream m_stream = null;

    
/// <summary>
    
/// Basic constructor that uses the Normal compression level
    
/// </summary>
    
/// <param name="baseStream">The stream to wrap up with the deflate algorithm</param>

    public DeflateFilter(Stream baseStream) : this(baseStream, CompressionLevels.Normal) { }

    
/// <summary>
    
/// Full constructor that allows you to set the wrapped stream and the level of compression
    
/// </summary>
    
/// <param name="baseStream">The stream to wrap up with the deflate algorithm</param>
    
/// <param name="compressionLevel">The level of compression to use</param>

    public DeflateFilter(Stream baseStream, CompressionLevels compressionLevel) : base(baseStream, compressionLevel) {
        m_stream 
= new DeflateStream(baseStream, CompressionMode.Compress);
    }


    
/// <summary>
    
/// Write out bytes to the underlying stream after compressing them using deflate
    
/// </summary>
    
/// <param name="buffer">The array of bytes to write</param>
    
/// <param name="offset">The offset into the supplied buffer to start</param>
    
/// <param name="count">The number of bytes to write</param>

    public override void Write(byte[] buffer, int offset, int count) {
      
if(!HasWrittenHeaders) WriteHeaders();
      m_stream.Write(buffer, offset, count);
    }


    
/// <summary>
    
/// Return the Http name for this encoding.  Here, deflate.
    
/// </summary>

    public override string ContentEncoding {
      
get return "deflate"; }
    }


    
/// <summary>
    
/// Closes this Filter and calls the base class implementation.
    
/// </summary>

    public override void Close() {
      m_stream.Close();
    }


    
/// <summary>
    
/// Flushes that the filter out to underlying storage
    
/// </summary>

    public override void Flush() {
      m_stream.Flush();
    }

  }

}

posted @ 2011-04-12 12:12  Shikyoh  阅读(1091)  评论(0编辑  收藏  举报