下載 sample code

大家都知道ViewState會以亂碼保存在Client端網頁上,所以不像Session會逾時,Server重開後會遺失,但是它多多少少會受限於網路的頻寬,畢竟你存越多東西在ViewState裡,Client端的網頁就會越龐大

(例如存個5000筆資料在ViewState裡,這樣影響就會很明顯)

所以,如果我們可以將ViewState保存在Server的硬碟裡,就可以改善這個問題,至少,目前加硬碟的成本比增加頻寬便宜

第一步,如下圖先加幾個檔案吧:





App_Browser 裡加入瀏覽資訊檔:




在瀏覽資訊檔(ViewStateAdapter.browser)裡,加上這一段:




再來,App_Code裡的ViewStateAdapter.vb程式碼如下:


     Imports Microsoft.VisualBasic

Public Class ViewStateAdapter
Inherits System.Web.UI.Adapters.PageAdapter '繼承 System.Web.UI.Adapters.PageAdapter

Public Overrides Function GetStatePersister() As System.Web.UI.PageStatePersister
'如果你要存在Session裡,寫這一句就行了,這是微軟內建的
'Return New System.Web.UI.SessionPageStatePersister(Me.Page)

'不過我們今天要自己寫個Persister,存到硬碟裡
Return New ViewStatePersister(Me.Page)

End Function
End Class





再來就是App_Code裡的ViewStatePersister.vb:架構如下





ViewStatePersister.vb程式碼如下:


     Imports Microsoft.VisualBasic



Public Class ViewStatePersister
Inherits System.Web.UI.PageStatePersister

'viewstate檔案存在這個路徑下
Private Const _strFilePathFormat As String = "~/PersistedViewState/{0}.vs.resource"

'使用Guid編一個唯一的viewstate檔案名稱
Private _strNewGuid As String = Guid.NewGuid().ToString()

'這是網頁上一個HiddenField的欄位名稱,viewstate的檔案名稱會保存在這裡
Private Const _strViewStateHiddenFieldName As String = "__ViewStateGuid"

Public Sub New(ByVal p As System.Web.UI.Page)
MyBase.New(p)
End Sub

''' <summary>
''' 重寫 Load 方法,從Server的硬碟裡讀取 ViewState
''' </summary>
Public Overrides Sub Load()

Dim strViewStateFilePath As String = Me.Page.Server.MapPath(String.Format(_strFilePathFormat, Me.Page.Request.Form(_strViewStateHiddenFieldName)))
' deserialize the base-64 encoded string into a view state
If Not IO.File.Exists(strViewStateFilePath) Then
Throw New Exception("保存 Viewstate 的文件 \" & strViewStateFilePath & " \ 不存在")
Else
' instantiates the formatter and opens the file
Dim objLosFormatter As LosFormatter = New LosFormatter()
Dim objStreamReader As IO.StreamReader = IO.File.OpenText(strViewStateFilePath)
Dim strViewState As String = objStreamReader.ReadToEnd()
objStreamReader.Close()

Dim statePair As Pair = CType(objLosFormatter.Deserialize(strViewState), Pair)
ViewState = statePair.First
ControlState = statePair.Second
End If

End Sub

''' <summary>
''' 重寫 Save 方法,保存 ViewState 到Server的硬碟裡
''' </summary>
Public Overrides Sub Save()
' serialize the view state into a base-64 encoded string
Dim objLosFormatter As LosFormatter = New LosFormatter()
Using objStringWriter As IO.StringWriter = New IO.StringWriter()
' save the view state to disk
Dim statePair As Pair = New Pair(ViewState, ControlState)
objLosFormatter.Serialize(objStringWriter, statePair)
WirteViewStateToFile(objStringWriter)
End Using
' saves the view state GUID to a hidden field
Page.ClientScript.RegisterHiddenField(_strViewStateHiddenFieldName, _strNewGuid)

End Sub

''' <summary>
''' 存檔的實際邏輯
''' </summary>
Private Sub WirteViewStateToFile(ByVal SW As IO.StringWriter)
If Not Me.Page.Request.Form(_strViewStateHiddenFieldName) Is Nothing Then
_strNewGuid = Me.Page.Request.Form(_strViewStateHiddenFieldName)
End If

Dim strViewStateFilePath As String = Me.Page.Server.MapPath(String.Format(_strFilePathFormat, _strNewGuid))
Try
Using objStreamWriter As IO.StreamWriter = IO.File.CreateText(strViewStateFilePath)
objStreamWriter.Write(SW.ToString())
End Using

Catch dnfEx As IO.DirectoryNotFoundException
IO.Directory.CreateDirectory(strViewStateFilePath.Substring(0, strViewStateFilePath.LastIndexOf("\")))
WirteViewStateToFile(SW)
End Try

End Sub

End Class




最後記得將PersistedViewState資料夾的安全性設定一下,這樣ASP.NET才寫的進去:





寫個網頁試試看:



看看原始碼,ViewState不見了
<input type="hidden" name="__ViewStateGuid" id="__ViewStateGuid"
value="c3c8b1f7-fd8f-412e-84bf-9684b895220b" />
<input type="hidden" name="__VIEWSTATE" id=" __VIEWSTATE" value="" />



因為他在這裡歐:



我們只要另外再寫個程式(例如HttpModule),定時清一下過期(例如兩天前)的ViewState檔案,這樣就很完美了

進一步的,我們還可以利用這種方式,做出其他許多ViewState應用,例如ViewState加密、壓縮等等,還蠻有趣的,哈

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 atratus 的頭像
    atratus

    The Jack's Files

    atratus 發表在 痞客邦 留言(3) 人氣()