[ASP.NET]使用SharpZipLib進行網頁ViewState壓縮

[ASP.NET]使用SharpZipLib進行網頁ViewState壓縮

上一篇我們提到透過IIS進行網頁的壓縮,但由於IIS本身能控制的只有response給client的流量(對client來說是下載),但我們在使用網路時都知道我們使用網路的瓶頸很多時候是在上傳的流量上,舉例來說:2M/512K,在過去的認知中很多人都說所以我們下載的流量應該可以達到2MB每秒,但實際上2M代表的事2M bits,所以實際換算下來的上下傳極速應該只有256K Bytes/64K Bytes,但根據中華電信的說法,大約只有1/10的用戶可以達到這個速度,當上傳的頻寬由我們認為的512降到只剩下64時,我們不能不正視使用者上傳所需頻寬的問題。

以下所提的是針對壓縮網頁的ViewState來進行上傳流量的縮減, 所謂的Viewstate講的白話一點就是網頁中用來記錄畫面狀態的隱藏欄位,只要網頁沒有禁用ViewState的話,ViewState的量會隨著網頁的複雜度而提高,而用ViewState用的最兇的元件就是DataGrid、GridView一類的元件,當每次postback時,ViewState的量愈大,所需上傳回Server的流量也愈大。

以下的做法透過SharpZipLib這個元件來進行,做法是先將ViewState序列化轉存到記憶體中,你可以針對ViewState量比較大的網頁作或者直接將以下這段程式碼寫到網頁的BasePage中,範例如下:


using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.IO;
using ICSharpCode.SharpZipLib.Zip.Compression;

namespace Compress
{
    /// <summary>
    /// BasePage 的摘要描述
    /// </summary>
    public class BasePage : Page
    {
        public BasePage()
        {
            //
            // TODO: 在此加入建構函式的程式碼
            //
        }


        /// <summary>
        /// 設定序列化後的字串長度為多少後啟用壓縮
        /// </summary>
        private static Int32 LimitLength = 10240;

        /// <summary>
        /// 設定壓縮比率,壓縮比率越高性消耗也將增大
        /// </summary>
        private static Int32 ZipLevel = ICSharpCode.SharpZipLib.Zip.Compression.Deflater.BEST_COMPRESSION;

        /// <summary>
        /// override掉Page中原來的SavePageStateToPersistenceMedium()
        /// </summary>
        /// <param name="pViewState">ViewState物件</param>
        protected override void SavePageStateToPersistenceMedium(Object pViewState)
        {
            //實現一個用於將資訊寫入字串的 TextWriter
            StringWriter mWriter = new StringWriter();

            //序列化 Web Page檢視狀態
            LosFormatter mFormat = new LosFormatter();
            mFormat.Serialize(mWriter, pViewState);

            //將序列化後的物件轉成Base64字串
            String vStateStr = mWriter.ToString();

            //設置是否啟用了加密方式,預設情況下為不啟用
            Boolean mUseZip = false;

            //判斷序列化物件的字串長度是否超出10K
            if (vStateStr.Length > LimitLength)
            {
                //如果ViewState大於30K就進行壓縮,同時將狀態設為加密方式
                mUseZip = true;

                Byte[] pBytes = Compress(vStateStr);

                //將位元組陣列轉換為Base64字串
                vStateStr = System.Convert.ToBase64String(pBytes);
            }

            //將壓縮後的ViewState存放到隱藏欄位中
            ClientScript.RegisterHiddenField("__MSPVSTATE", vStateStr);

            //將是否啟用壓縮狀態存放到隱藏欄位中
            ClientScript.RegisterHiddenField("__MSPVSTATE_ZIP", mUseZip.ToString().ToLower());
        }

        /// <summary>
        /// 對字串進行壓縮
        /// </summary>
        /// <param name="pViewState">ViewState字串</param>
        /// <returns>返回流的位元組陣列</returns>
        public static Byte[] Compress(String pViewState)
        {
            //將字串轉換為位元組陣列
            Byte[] pBytes = System.Convert.FromBase64String(pViewState);

            //建立記憶體的Stream
            MemoryStream mMemory = new MemoryStream();

            Deflater mDeflater = new Deflater(ZipLevel);
            ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream mStream = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream(mMemory, mDeflater, 131072);

            mStream.Write(pBytes, 0, pBytes.Length);
            mStream.Close();

            return mMemory.ToArray();
        }

        /// <summary>
        /// 重寫將所有保存的視圖狀態資訊載入到頁面物件
        /// </summary>
        /// <returns>保存的ViewState</returns>
        protected override Object LoadPageStateFromPersistenceMedium()
        {
            //使用Request方法獲取序列化的ViewState字串
            String mViewState = this.Request.Form.Get("__MSPVSTATE");
            //使用Request方法獲取當前的ViewState是否啟用了壓縮
            String mViewStateZip = this.Request.Form.Get("__MSPVSTATE_ZIP");

            Byte[] pBytes;

            //如果有壓縮的話,才解壓縮,否則就直接轉成位元組
            if (mViewStateZip == "true")
            {
                pBytes = DeCompress(mViewState);
            }
            else
            {
                //將ViewState的Base64字串轉換成位元組
                pBytes = System.Convert.FromBase64String(mViewState);
            }

            //序列化 Web 表單頁的ViewState
            LosFormatter mFormat = new LosFormatter();

            //將指定的檢視狀態值轉換為有限物件序列化 (LOS) 格式化的物件
            return mFormat.Deserialize(System.Convert.ToBase64String(pBytes));
        }

        /// <summary>
        /// 解壓縮ViewState字串
        /// </summary>
        /// <param name="pViewState">ViewState字串</param>
        /// <returns>返回流的位元組陣列</returns>
        public static Byte[] DeCompress(String pViewState)
        {
            //將Base64字串轉換為位元組陣列
            Byte[] pBytes = System.Convert.FromBase64String(pViewState);

            ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream mStream = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream(new MemoryStream(pBytes));

            //創建支援記憶體存儲的流
            MemoryStream mMemory = new MemoryStream();
            Int32 mSize;

            Byte[] mWriteData = new Byte[4096];

            while (true)
            {
                mSize = mStream.Read(mWriteData, 0, mWriteData.Length);
                if (mSize > 0)
                {
                    mMemory.Write(mWriteData, 0, mSize);
                }
                else
                {
                    break;
                }
            }

            mStream.Close();
            return mMemory.ToArray();
        }
    }
}

接著我們的頁面再去繼承Compress.BasePage:

image

 

以下是繼承Page時,100筆資料Postback時的ViewState量,大約是40KB:

image

 

而修改為繼承Compress.BasePage後則變成只剩下9KB左右,節省的結果非常可觀:

image

 

不管使用網頁壓縮或者ViewState壓縮,對 Server的CPU與記憶體都會造成某種程度的影響,但在現今的環境下,硬體的價格遠低於網路頻寬的價格,將負載放在Server上是一個蠻普遍的做法,許多大型的入口網頁都有進行網頁的壓縮,因為當我們所要提供的服務愈來愈複雜時,很難要求我們把程式做的功能超強、操作性極佳又效能超好。

透過以下的網頁我們可以知道每個網站是否有使用網頁壓縮跟它壓縮的技術:http://www.port80software.com/tools/compresscheck.asp,像點部落:

image

Yahoo首頁:

image

游舒帆 (gipi)

探索原力Co-founder,曾任TutorABC協理與鼎新電腦總監,並曾獲選兩屆微軟最有價值專家 ( MVP ),離開職場後創辦探索原力,致力於協助青少年培養面對未來的能力。認為教育與組織育才其實息息相關,都是在為未來儲備能量,2018年起成立為期一年的專題課程《職涯躍升的關鍵24堂課》,為培養台灣未來的領袖而努力。