頁面文件比較大時,開啟GZip壓縮可以減少傳輸的流量,測試了一下效果非常明顯,看了電腦軟硬件應用網網友裡面有些人使用了IIS的GZip壓縮功能,項目中也應用了該功能,使用的過程中遇到一個比較棘手的問題,找了挺久的一直搞不定。
項目中在服務器端生成了Excel2007報表文件,文件在下載到本地後不能正常打開了,提示文件格式有問題,由於Excel2007文件格式的本質是一壓縮包,懷疑文件在下載的過程中被改動了,但是具體不知道在哪個環節上出了問題。
猜想:
1.因為Excel2007文件是在頁面Render的時候輸出的,並且輸出完後直接End請求,可以推斷IIS的壓縮機制,應該是在請求開始的時候就已經加入,而不是在請求結束的時候進行壓縮的。
2.文件下載時直接彈出下載框進行下載,可能沒有經過浏覽器的解壓過程。這一點有待考證。抑或有其他原因導致了不能對壓縮包進行加壓,因為這個壓縮包是經過兩次打包過的(Excel2007文件生成的時候打包過一次,在通過IIS下載時又被打包了一次)。
使用IIS壓縮的問題(目前發現的):
1.配置不方便,要更改系統裡面的那個配置文件,權限不夠的話沒辦法配置。
2.IIS6下會影響到IIS內的所有站點,據說IIS7不存在這個問題。
3.配置了IIS壓縮的機器,在重啟幾次後配置丟失了(怪事)。
4.Excel2007下載的問題,估計OOXML格式的問題都會有問題。
解決:
解決的辦法就是使用HttpModule進行壓縮,在HttpModule中使用PostReleaseRequestState事件進行壓縮,將壓縮的時機延後到Render之後,通常的代碼都不會卸載Render之後了吧 :)
既然PostReleaseRequestState事件是在Render之後,有人會有疑問了,那也可以在頁面裡面訂閱啊,在我實際的測試中發現是不能在頁面邏輯中改變Response.Filter,會報錯的,這個可能涉及到asp.net中的一些權限吧,所以還是老老實實的在HttpModule中去實現吧。
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.IO.Compression;
namespace CapabilityTest
{
public class CompressModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
//throw new NotImplementedException();
}
public void Init(HttpApplication context)
{
context.PostReleaseRequestState += new EventHandler(context_PostReleaseRequestState);
}
#endregion
private const string GZIP = "gzip";
private const string DEFLATE = "deflate";
private void context_PostReleaseRequestState(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
Stream filter = app.Response.Filter;
if (IsEncodingAccepted(app.Request, GZIP))
{
app.Response.Filter = new GZipStream(filter, CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", GZIP);
}
else if (IsEncodingAccepted(app.Request, DEFLATE))
{
app.Response.Filter = new DeflateStream(filter, CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", DEFLATE);
}
}
private static bool IsEncodingAccepted(HttpRequest request, string encoding)
{
string acceptEncoding = request.Headers["Accept-Encoding"];
if (acceptEncoding == null)
return false;
acceptEncoding = acceptEncoding.ToLower();
if (encoding == GZIP)
return acceptEncoding.Contains(GZIP) || acceptEncoding.Contains("x-gzip") || acceptEncoding.Contains("*");
if (encoding == DEFLATE)
return acceptEncoding.Contains(DEFLATE);
return false;
}
}
}
改進:
1.可以在config文件中加入自定義的ConfigurationSection,使用配置的方式實現對指定的文件擴展名進行壓縮,或排除對指定的文件擴展名的壓縮。
2.網絡上看到有人說要針對微軟的AJAX做處理,不能進行壓縮,判斷的方法:
return (app.Request["HTTP_X_MICROSOFTAJAX"] != null
|| app.Request["Anthem_CallBack"] != null);
3.可針對實現了某個基類或接口的頁面進行壓縮或不壓縮:
ICompressable p = app.Context.Handler as ICompressable;
return (p == null);
此外:
本文針對IIS6,最新的IIS7沒有親身體驗過,不知道關於壓縮方面做得好不好。文中提到的PostReleaseRequestState事件只有在asp.net 2.x才開始支持,故不能用於vs2003開發的項目哦。