回顧上一篇:
我們介紹了如何從HTTP請求流中將資料部分進行截取,同時將資料相關資訊進行儲存。
本篇概述:
用過ajax的朋友應該有聽過XmlHttpRequest對象,ajax其實就是通過XmlHttpRequest對象來向伺服器發出非同步請求,並從伺服器獲得資料,然後用javascript來操作DOM而更新頁面。
本篇就是要通過XmlHttpRequest對象來實現即時的進度顯示。
:
本文部分:
看過有些前輩的做法是通過設定HTTP請求的Refresh頭欄位來定時重新整理頁面從而顯示進度,但是這樣就會帶動整個頁面一起重新整理,就算我們把進度條做成單獨的頁面,效果仍舊不是太好。我之前試過用ajax的Timer組件,但是不知道是何原因,Timer控制項在IIS下預覽時總是無法正常發揮作用。苦惱了好一陣子,懷疑是MS的BUG。最後發現了一個很好的替代辦法就是利用XmlHttpRequest對象來自己實現定時重新整理,這樣每次只需向伺服器請求很少的資料,減少了對伺服器的壓力,在後期的測試中,發現這個辦法確實很好用,而且在IIS下也一切正常(就是IIS下啟動並執行效果)。
當然如果光有進度條沒有資料,那這個進度條也只能是個擺設,所以我把接下來的內容分成兩塊:進度資訊的儲存、進度的顯示
1、進度資訊的儲存
首先我們要明白進度條在這裡反應的是什麼的進度?毫無疑問是檔案上傳的進度嘍~~在上一篇中,我們對上傳的檔案資料進行了提取,也就是說這個提取的進度就是我們要顯示給用戶端的進度。那就簡單了,我們只要把已經提取的檔案大小與總的檔案大小比對一下,就可以知道完成的百分比了。可是問題來了,我們如何知道上傳了多少了呢?答案肯定是要用一個變數來儲存已經上傳的資料量。那這個變數要放在哪裡才能讓我們既可以在進度頁面中訪問,又可以在HTTP上傳模組中訪問呢?
大家肯定知道一般情況下,使用者在多個頁面之間訪問,會用到Session對象或URL傳值來進行頁面之前的通訊。但是前一篇所介紹的HTTP模組並不屬於一個頁面,因此我們無法簡單的應用Session讓進度頁面與上傳模組實現通訊。這裡主要還是借鑒高山來客的思路:首先構建一個用於存放檔案資訊的類,該類主要用來儲存檔案資訊,如:檔案名稱,路徑,當前上傳的資料量,上傳時間等。然後設定一個針對某次上傳的唯一ID做為頁面中通訊的暗號,擁有這個暗號的頁面才能擷取對應於某次上傳的檔案資訊。現在已經有了兩個變數了,接著就要使這兩個變數可以被多個頁面所使用,方法就是在上傳頁面中,將這個ID變數註冊為該頁面的一個隱藏欄位,這樣包含這個頁面的HTTP請求流中就會包含那個上傳ID。另一個類變數就儲存在頁面緩衝Cache中,並用上傳ID做為其編號。
現在假設已經有了這麼一個用於存放檔案資訊的類UploadFileInfo。
首先我們要在上傳頁面的PageLoad中new一個ID,然後註冊一個隱藏欄位用來儲存此ID,同時執行個體化UploadFileInfo類,並將相應的資訊寫入該類,最後把該類放入Catch:
if (!IsPostBack)
{
UploadFileInfo ufi = new UploadFileInfo();
ufi.strFileGuid = Guid.NewGuid().ToString;//用GUID來表示唯一的ID;
ufi.strTempDir = Server.MapPath("TempUpload/" + ufi.strFileGuid + "//");
ClientScript.RegisterHiddenField("UploadID", ufi.strFileGuid);//隱藏欄位,名字為UploadID,值為ufi.strFileGuid
HttpContext.Current.Cache.Add(ufi.strFileGuid, ufi, null, DateTime.Now.AddDays(10), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, null);//加入到Catch中
}
經過以上步驟,我們就可以在HTTP模組中訪問了。
因為在這次的HTTP請求流中包含了一個隱藏欄位,所以我們可以對擷取的HTTP請求流進行分析,從而擷取相應的上傳ID,也就是我們之前說的暗號。然後通過Cache的編號找到Cache中的檔案資訊對象,從而我們可以在後來的資料讀取過程中對該對象的上傳資料量進行修改。由於是放在Cache中,加之是一個引用對象,所以對該對象修改後,其它代碼訪問到的都是最新的值。
string sguid = GetUploadId(bPreloadedEnitityBody, eContentEncode);//GetUploadId是自己寫的一個方法用來從請求流中擷取上傳ID
UploadFileInfo ufiFileInfo = (UploadFileInfo)HttpContext.Current.Cache[sguid];//取出檔案資訊對象
其它頁面如果要使用這個對象就得先擷取ID,之後就可以自由操作了。
2、進度的顯示
我們可以看到,當顯示進度的時候,背後的頁面成灰色,並且無法響應任何事件,有點類似模態視窗。這個效果大家可以在網上查查,還是挺容易實現的。我這裡有一段js顯示此效果的代碼(搜集於網上):
ModalDialog
接著講我們的重點:如何?定時局部重新整理。
關於XmlHttpRequest對象,我這裡就不詳細講述了,提供大家一個關於此的手冊下載。為了大家更容易理解,我舉個小例子:
小例子
通過以上小例子,大家應該已經對該對象有所瞭解了吧。為實現定時重新整理,我把進度條單獨放在一個頁面中(如A.aspx),通過js的setTimeout來定時執行類似returnresponse這樣的方法,然後在A.aspx.cs代碼中擷取檔案資訊對象,接著通過Response來反饋進度資訊。這樣在A.aspx頁面中就可以擷取到資訊,並進行顯示了。但是執行ActiveXObject將要花費不少代價,而且我們是定時執行該方法,顯然會造成效能下降。在參考了構建一個pool來管理無重新整理頁面的xmlhttp對象後,決定採用這一方法,事實證明該方法確實有效。
利用pool後的代碼
到這就差不多整個專題都結束了,接下來幾天,我會把代碼稍微調整下,然後傳上來。
由於這段時間要上班,實在抽不出時間來整理,如果大家需要可以先拿去看看。不過代碼寫的有點亂,而且有些功能也沒有完善,時間實在太少,大家見諒。
粗糙的工程