利用ajax技術顯示上傳進度

回顧上一篇

      我們介紹瞭如何從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)
ExpandedBlockStart.gif
{
    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顯示此效果的代碼(蒐集於網上):

ContractedBlock.gifModalDialog


      接着講我們的重點:如何實現定時局部刷新。
      關於XmlHttpRequest對象,我這裏就不詳細講述了,提供大家一個關於此的手冊下載。爲了大家更容易理解,我舉個小例子:
  

ContractedBlock.gif小例子

    

      通過以上小例子,大家應該已經對該對象有所瞭解了吧。爲實現定時刷新,我把進度條單獨放在一個頁面中(如A.aspx),通過js的setTimeout來定時執行類似returnresponse這樣的方法,然後在A.aspx.cs代碼中獲取文件信息對象,接着通過Response來反饋進度信息。這樣在A.aspx頁面中就可以獲取到信息,並進行顯示了。但是執行ActiveXObject將要花費不少代價,而且我們是定時執行該方法,顯然會造成性能下降。在參考了構建一個pool來管理無刷新頁面的xmlhttp對象後,決定採用這一方法,事實證明該方法確實有效。

ContractedBlock.gif利用pool後的代碼

       到這就差不多整個專題都結束了,接下來幾天,我會把代碼稍微調整下,然後傳上來。 

       由於這段時間要上班,實在抽不出時間來整理,如果大家需要可以先拿去看看。不過代碼寫的有點亂,而且有些功能也沒有完善,時間實在太少,大家見諒。
       粗糙的工程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章