原文地址:http://www.dotnetbips.com/articles/22d33d11-1a75-42c8-bbf6-ca1a345d3fcf.aspx
[原文源碼下載]
[翻譯]asp.net 2.0中通過壓縮ViewState改善效能
原文發布日期:2007.03.08
作者:Bipin Joshi
翻譯:webabcd
介紹
開發人員經常擔心他們web網站的效能。每一個開發人員都想他們的web網站的效能是最佳化的。影響你web網站效能的有很多因素,ViewState就是其中之一。本文我將給大家提供一個通過壓縮ViewState來改善效能的方法。
什麼是ViewState
雖然本文並不是專門來研究ViewState的,但我們還是簡單的討論一下吧。你如果看過web form產生的HTML代碼的話,就會發現在一個名為__VIEWSTATE的隱藏欄位。聰明的ASP.NET會持久化這些控制項的值到這個隱藏欄位中,這對於往返伺服器的過程中儲存控制項的值是非常有用的。但是,此時ViewState會帶來效能問題。因為ViewState要在服務端與用戶端之間傳輸,所以會增加網路頻寬的流量。
要減少ViewState的話,可以關閉ViewState,就是設定控制項的EnableViewState屬性,如果設定該屬性為false則控制項的ViewState將被關閉。但是,如果沒有ViewState的話,控制項狀態持久化的工作就需要你自己來做了,這將是令人非常頭痛的。還有另一種減少ViewState的方法就是本文將要介紹的方法,即在ViewState傳輸之前先壓縮它,這樣ViewState的資料將會大幅度減少。
在ASP.NET 1.x中如果我們要使用壓縮功能的話需要自己寫一些代碼。而現在.NET 2.0提供了System.IO.Compression命名空間,將會使壓縮功能的實現變得非常簡單。System.IO.Compression命名空間的GZipStream類可以處理流的壓縮和解壓,注意,GZipStream只能以流的方式工作。GZipStream類不知專門壓縮ViewState而設計的,所以我們為了實現ViewState的壓縮和解壓要自己寫一些代碼。
開發ViewStateHelper
我們首先用Visual Studio建立一個web網站,然後再App_Code檔案夾中增加一個名為ViewStateHelper的類。類檔案頂部要引入的命名空間如下using System.IO;
using System.IO.Compression;
System.IO命名空間為我們提供了流的類,如MemoryStream。The System.IO.Compression命名空間為我們提供GZipStream類,它允許你呈現gzip格式的資料(RFC 1952)。
壓縮資料
現在增加一個名為Compress()的方法如下public static byte[] Compress(byte[] data)
{
MemoryStream ms = new MemoryStream();
GZipStream stream = new GZipStream(ms, CompressionMode.Compress);
stream.Write(data, 0, data.Length);
stream.Close();
return ms.ToArray();
}
這個Compress()靜態方法接收一個位元組數群組類型的變數並壓縮它,返回的是一個壓縮後的位元組數組。它首先執行個體話一個MemoryStream類,它用來呈現記憶體中的流。然後通過MemoryStream和壓縮模式兩個參數建立一個GZipStream對象。你可以用任何流類型來替換MemoryStream。被壓縮的資料將寫進這個流。GZipStream類的Write()方法會接收一個位元組數群組類型的變數,然後壓縮它並寫進流中(在我們的例子中是MEmoryStream)。寫完之後GZipStream被關閉。最後,MemoryStream的ToArray()方法將轉換流資料到位元組數組中。
解壓資料
為瞭解壓資料,我們要增加另一個名為Decompress()的方法,其關鍵代碼如下public static byte[] Decompress(byte[] data)
{
MemoryStream ms = new MemoryStream();
ms.Write(data, 0, data.Length);
ms.Position = 0;
GZipStream stream = new GZipStream(ms, CompressionMode.Decompress);
MemoryStream temp = new MemoryStream();
byte[] buffer = new byte[1024];
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
{
break;
}
else
{
temp.Write(buffer, 0, buffer.Length);
}
}
stream.Close();
return temp.ToArray();
}
這個Decompress()靜態方法接收一個已壓縮的位元組數組,返回一個解壓後的位元組數組。首先,它建立了一個MemoryStream對象,並將一個已壓縮的位元組數組寫進去。然後這個流將作為參數提供給GZipStream的建構函式,注意此時的壓縮模式為Decompress。解壓後的資料也需要在某個地方儲存,所以接下來建立的另一個MemoryStream對象就是作此用途。接著我們用一個while迴圈從GZipStream讀取出解壓後的資料,每一塊都是1024位元組。然後這個資料就被寫進MemoryStream了。最後,GZipStream被關閉,解壓後的內容作為一個位元組數組被返回。
建立一個web form
現在開啟一個web form,拖拽一個SqlDataSource控制項到上面,配置它以使其可以從Northwind資料庫中讀取Customers表的記錄。
拖拽一個GridView控制項並設定它的DataSourceID屬性為你剛才配置好的SqlDataSource控制項的ID。確保運行這個web form是我們想要的結果,樣本如下
定製ViewState的序列化和還原序列化
你不用關心ViewState的序列化和還原序列化,因為這些都是自動的。但是因為咱們需要壓縮ViewState,所以需要定製它的序列化和還原序列化。Page基類有兩個虛擬方法,SavePageStateToPersistenceMedium()和LoadPageStateFromPersistenceMedium()。它們分別允許你定製ViewState的序列化和還原序列化。
我們首先在web form的後置代碼中重寫SavePageStateToPersistenceMedium()方法。在這個方法中,你將壓縮ViewState並儲存它到隱藏欄位中。代碼如下protected override void SavePageStateToPersistenceMedium(object state)
{
LosFormatter formatter = new LosFormatter();
StringWriter writer = new StringWriter();
formatter.Serialize(writer, state);
string viewState = writer.ToString();
byte[] data = Convert.FromBase64String(viewState);
byte[] compressedData = ViewStateHelper.Compress(data);
string str = Convert.ToBase64String(compressedData);
ClientScript.RegisterHiddenField("__MYVIEWSTATE", str);
}
SavePageStateToPersistenceMedium()方法接收一個ViewState對象。在該方法中建立一個LosFormatter對象,LosFormatter類允許你序列化和還原序列化ViewState中的資料。LosFormatter類的Serialize()方法接收兩個參數,分別是一個流和一個對象,結果是把這個對象序列化到這個流中。在我們的例子裡,ViewState被序列化到了StringWriter裡。StringWriter的ToString()方法返回一個字串用於呈現ViewState資料。這個字串是Base64格式的(在ASP.NET中就是這麼儲存到ViewState中的),我們需要把它轉換成我們想要的格式。Convert類的FromBase64String()方法就可以做這個工作。FromBase64String()方法返回一個位元組數組資料,然後使用我們的ViewStateHelper類的Compress()方法壓縮它。然後使用Convert類的ToBase64String()方法把被壓縮的資料轉換成Base64格式。最後,我們用ClientScript的RegisterHiddenField()方法把這個已壓縮的Base64格式的資料註冊到一個名為__MYVIEWSTATE的隱藏欄位中。
這就是我們壓縮ViewState的方法。另外還有一個同等重要的工作就是解壓,我們通過重寫LoadPageStateFromPersistenceMedium()方法來實現這樣的功能,其代碼如下protected override object LoadPageStateFromPersistenceMedium()
{
string viewstate = Request.Form["__MYVIEWSTATE"];
byte[] data = Convert.FromBase64String(viewstate);
byte[] uncompressedData =
ViewStateHelper.Decompress(data);
string str = Convert.ToBase64String(uncompressedData);
LosFormatter formatter = new LosFormatter();
return formatter.Deserialize(str);
}
這個方法首先讀出__MYVIEWSTATE隱藏欄位的值,該值為Base64格式的被壓縮ViewState。為瞭解壓它,你的第一步工作仍然是把它轉換為一個位元組數組,我們通過Convert類的FromBase64String()方法來做。接下來我們用ViewStateHelper類的Decompress()方法解壓資料。然後用Convert類的ToBase64String()方法再一次把解壓後的資料轉換為Base64格式的字串。最後,LosFormatter對象還原序列化解壓後的ViewState
以上就是壓縮和解壓ViewState的全部內容。經過我對本例的實際測試發現未經壓縮的ViewState大小為13,568位元組,壓縮後的ViewState大小為5,932位元組。這對於改善效能是非常有用的,你說呢?
總結
從本文中你學到了如何通過壓縮ViewState來改善你web網站的效能。GZipStream類提供了現成的把你的資料壓縮成gzip格式的方法。被壓縮的資料需要通過重寫Page基類的SavePageStateToPersistenceMedium()方法儲存。相似的,讀取ViewState後我們需要通過重寫LoadPageStateFromPersistenceMedium()來解壓資料。
作者:Bipin Joshi
Email:http://www.dotnetbips.com/contact.aspx
簡介:Bipin Joshi是DotNetBips.com的管理員。他是http://www.binaryintellect.com/的發起人,這個公司提供.NET framwork的培訓和諮詢服務。他在印度孟買為開發人員提供培訓。他也是微軟的MVP(ASP.Net)和ASPInsiders的會員。