一
在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 ...{

get...{ return _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();
}
}
}