让UpdatePanel支持文件上传(4):数据传输与解析机制
現在就要開始整個項目中最有技巧的部分了。如果我們的組件需要在多種瀏覽器中正常的運行,我們必須好好考慮一下發送和解析數據的方式。如果我們把這部分的機制完全交給ASP.NET AJAX原有的行為來執行,則會遇到問題。下面的代碼片斷就是IE 7和FireFox在收到服務器端的數據之后,iframe中的DOM結構:
<html><head></head><body><pre>33|updatePanel|ctl00_Main_UpdatePanel1|...</pre></body></html>很顯然,這段代碼的意圖是為了在頁面中直接顯示服務器端發送過來的數據。在這種情況下,我們就可以通過“<pre />”元素的innertText屬性(IE 7)或者textContent屬性(FireFox)來直接獲得這段文字。不幸的是,IE6的行為非常奇怪,與前兩者可謂大相徑庭。IE 6會把這段文字按照XML來解析,接著很自然的顯示出錯誤信息,告訴我們這段文本不是一個有效的XML文檔。這非常不合理,因為Response的“Content-Type”是“text/plain”而不是“text/xml”。這是我們要兼容多個瀏覽器時最頭疼的情況。
還記得我們在向客戶段輸出真實的數據前后都調用了WriteScriptBlock方法嗎?下面就是這個方法的實現:
internal static class AjaxFileUploadUtility {internal static void WriteScriptBlock(HttpResponse response, bool begin){string scriptBegin = "<script type='text/javascript' language='javascript'>window.__f__=function(){/*";string scriptEnd = "*/}</script>";response.Write(begin ? scriptBegin : scriptEnd);} }IE 6和IE 7會將使用<script />來包含的文本作為一段腳本代碼來處理。我們這里在真實的數據兩邊加上了腳本定義的內容,使它成為了客戶端iframe中“__f__”方法的一段注釋,因此我們可以通過調用這個方法的toString函數來獲得這個方法的文本內容。請注意在RenderPageCallback方法中,我們把文本進行了編碼,將“*/”替換為“*//*”,然后再將其發送到客戶端,這么做的目的是使這段文本能夠成為合法的JavaScirpt代碼。
StringBuilder sb = new StringBuilder(); HtmlTextWriter innerWriter = new HtmlTextWriter(new StringWriter(sb)); renderPageCallbackMethodInfo.Invoke(this.PageRequestManager, new object[] { innerWriter, pageControl });writer.Write(sb.Replace("*/", "*//*").ToString());等一下,我們在這里把異步刷新運行正常時輸出的文本進行了編碼,但是我們在異常情況下的輸出并沒有這么做,不是嗎?沒錯。因為在異常狀況下,錯誤信息會通過Response的Write方法直接輸出(請看PageRequestManager類的OnPageError方法),因此我們無法向前面的代碼那樣獲得它輸出的結果。我們現在只能希望錯誤信息中不要出現“*/”這樣的字符串吧(當然,我們可以使用反射機制來重寫整個邏輯,但是這樣做實在比較復雜)。
下面,我們就要在客戶端的_iframeLoadComplete方法中重新獲取這段文本了:
_iframeLoadComplete : function() {//...try{ var f = iframe.contentWindow.__f__;var responseData = f ? : ;if (responseData.indexOf("\r\n") < 0 && responseData.indexOf("\n") > 0){responseData = responseData.replace(/\n/g, "\r\n");}this._responseData = responseData;this._statusCode = 200;this._responseAvailable = true;}catch (e){this._statusCode = 500;this._responseAvailable = false;}// ... },_parseScriptText : function(scriptText) {var indexBegin = scriptText.indexOf("/*") + 2;var indexEnd = scriptText.lastIndexOf("*/");var encodedText = scriptText.substring(indexBegin, indexEnd);return encodedText.replace(/\*\/\/\*/g, "*/"); },我們在這里將判斷iframe的window對象中是否存在“__f__”方法,而不是直接判斷瀏覽器的類型來決定下面要做的事情,因為這樣可以帶來更多的瀏覽器兼容性。
FireFox的行為則完全不是這樣的,它依舊使用我們一開始提到的那種DOM結構,把從服務器端得到的文本顯示在iframe中,這種做法比較合理,因為Response的Content-Type為“text-plain”。因此,我們會使用另一種方法來得到這段文本:
_parsePreNode : function(preNode) {if (preNode.tagName.toUpperCase() !== "PRE") throw new Error();return this._parseScriptText(preNode.textContent || preNode.innerText); },請注意,“_iframeLoadComplete”方法中還有幾行非常重要的代碼:
if (responseData.indexOf("\r\n") < 0 && responseData.indexOf("\n") > 0) {responseData = responseData.replace(/\n/g, "\r\n"); }由于從服務器端得到的腳本將會被分割為多個部分,每個部分的格式為“length|type|id|content”,因此字符串的長度是在解析文本時非常重要的屬性。因此,我們將會把所有的“\r”替換成“\r\n”,以此保持內容和長度的一致,否則解析過程將會失敗。而且事實上,這樣的替換只會出現在FireFox中。(未完待續)
?
點擊這里下載整個項目
English Version
總結
以上是生活随笔為你收集整理的让UpdatePanel支持文件上传(4):数据传输与解析机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python函数的继承_Python 继
- 下一篇: des加解密java c#,C#编写DE