[原創]FineUI秘密花園(七) — 上傳控件


FineUI直到V3.0才內置了自己的上傳控件,為什么唯獨上傳控件姍姍來遲,這其中的緣由是啥?之前又是如何實現上傳功能的呢?下面聽我慢慢道來。

 

AJAX請求與文件上傳請求的對比

 

普通的AJAX請求的請求頭和請求正文:

image

 

image

 

文件上傳請求的請求頭與請求正文:

image

image

 

可見,普通的AJAX請求其Content-Type為application/x-www-form-urlencoded,其請求正文是鍵值對組成的查詢字符串;而文件上傳請求其Content-Type為multipart/form-data,其請求正文不僅分段包含正常的表單字段,而且包含要上傳文件的內容。

 

也就是說兩種請求方式完全不同,要在AJAX環境中實現文件上傳,最通用的做法是用一個臨時生成的IFrame來提交文件。而這個過程將會比較復雜,這也是FineUI一直沒有實現自己的文件上傳控件的原因(后來發現ExtJS支持這個過程,所以就有了FineUI自己的FileUpload控件)。

 

 

之前實現文件上傳的方式

由於之前FineUI沒有自己的FileUpload控件,只好求助於ASP.NET的FileUpload控件,並且由於文件上傳的請求不能是AJAX的請求,所以只好采用整個頁面回發的方式(這也是很多網友所抱怨的地方),參見這個示例

   1:  <ext:PageManager ID="PageManager1" runat="server" EnableAjax="false" />
   2:  <asp:FileUpload ID="FileUpload1" runat="server"></asp:FileUpload>
   3:  <asp:Button ID="btnCloseWindow2" runat="server" Text="上傳文件" OnClick="btnCloseWindow2_Click"></asp:Button>

 

   1:  protected void btnCloseWindow2_Click(object sender, EventArgs e)
   2:  {
   3:      if (FileUpload1.HasFile)
   4:      {
   5:          FileUpload1.SaveAs(Server.MapPath("~/upload/" + FileUpload1.FileName));
   6:      }
   7:      Alert.ShowInTop("文件上傳成功!");
   8:  }

 

顯示效果如下圖所示:

image

 

正如前文所述,這個實現有兩個突出問題:1. 文件上傳框風格和整個頁面風格不搭配。 2. 上傳時是整個頁面回發,和FineUI默認的AJAX風格也不搭。

 

 

現在的文件上傳方式

現在就簡單多了,並且也漂亮多了,參考這個示例

image

 

   1:  <ext:SimpleForm ID="SimpleForm1" BodyPadding="5px" runat="server" EnableBackgroundColor="true"
   2:      ShowBorder="True" Title="表單" Width="350px" ShowHeader="True">
   3:      <Items>
   4:          <ext:TextBox runat="server" Label="用戶名" ID="tbxUseraName" Required="true" ShowRedStar="true">
   5:          </ext:TextBox>
   6:          <ext:FileUpload runat="server" ID="filePhoto" EmptyText="請選擇一張照片" Label="個人頭像" Required="true"
   7:              ShowRedStar="true">
   8:          </ext:FileUpload>
   9:          <ext:Button ID="btnSubmit" runat="server" OnClick="btnSubmit_Click" ValidateForms="SimpleForm1"
  10:              Text="提交">
  11:          </ext:Button>
  12:      </Items>
  13:  </ext:SimpleForm>
 
   1:  protected void btnSubmit_Click(object sender, EventArgs e)
   2:  {
   3:      string fileName = DateTime.Now.Ticks.ToString() + "_" + filePhoto.FileName;
   4:      if (filePhoto.HasFile)
   5:      {
   6:          filePhoto.SaveAs(Server.MapPath("~/upload/" + fileName));
   7:      }
   8:  }
 

下面來看看FileUpload的屬性:

  1. ButtonText:按鈕文本。
  2. ButtonOnly:是否只顯示按鈕,不顯示只讀輸入框。
  3. ButtonIcon:按鈕圖標。
  4. ButtonIconUrl:按鈕圖標地址。
  5. PostedFile:上傳的文件。
  6. HasFile:是否包含文件。
  7. FileName:上傳文件名。

 

還有一個重要的方法 SaveAs,用來將上傳的文本保存到服務器上。

 

 

太棒了太棒了太棒了太棒了太棒了

觀察文件上傳的過程

在文章的最開始我們提到,ExtJS是通過臨時創建一個IFrame來提交文件請求的,下面就通過一個示例來仔細觀察這個過程。

首先在Firefox中打開示例頁面: http://extasp.net/form/fileupload.aspx

打開Firebug,啟用腳本調試功能,在右側搜索框中輸入 Ext.extend(Ext.data.Connection ,將定位到Ext.ajax.request函數,所有的AJAX請求都是由這個函數發出了。

image

 

注意往下觀察,會看到這樣的代碼:

   1:  url = url || form.action; 
   2:  if(o.isUpload || (/multipart\/form-data/i.test(form.getAttribute("enctype")))) {
   3:          return me.doFormUpload.call(me, o, p, url);
   4:  }

很明顯,文件上傳的代碼都是在doFormUpload函數內完成的,下面是此函數的簡化版本(已經刪除了很多不影響理解的代碼):

 
           
   1:  doFormUpload: function (o, ps, url) {
   2:      var id = Ext.id(),
   3:          doc = document,
   4:          frame = doc.createElement('iframe'),
   5:          form = Ext.getDom(o.form),
   6:          encoding = 'multipart/form-data';
   7:   
   8:      Ext.fly(frame).set({
   9:          id: id,
  10:          name: id,
  11:          cls: 'x-hidden'
  12:      });
  13:      doc.body.appendChild(frame);
  14:   
  15:      Ext.fly(form).set({
  16:          target: id,
  17:          method: POST,
  18:          enctype: encoding,
  19:          encoding: encoding,
  20:          action: url || buf.action
  21:      });
  22:   
  23:      function cb() {
  24:          var me = this,
  25:              r = {
  26:                  responseText: '',
  27:                  responseXML: null,
  28:                  argument: o.argument
  29:              },
  30:              doc, firstChild;
  31:          doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
  32:          if (doc) {
  33:              r.responseText = doc.body.innerHTML;
  34:              r.responseXML = doc.XMLDocument || doc;
  35:          }
  36:          me.fireEvent(REQUESTCOMPLETE, me, r, o);
  37:      }
  38:      Ext.EventManager.on(frame, LOAD, cb, this);
  39:      form.submit();
  40:  }

下面我們來分析一下這個函數表達了哪些思想:

1. 首先第8行到13行是創建一個空的iframe標簽,並追加到頁面的底部。此時的頁面如下圖所示:

image

 

2. 第15到21行非常重要,注意target: id這個設置,其實是將頁面中表單的提交目標改變為當前窗體中新創建的iframe,而不是當前窗體了。

 

3. 第23行到37行定義回調函數cb。

 

4. 第38行將回調函數注冊為iframe加載完畢時執行的函數。

 

5. 第39行提交表單。注意,此時表單是在新創建的iframe中提交的!

 

6. 再來看看回調函數cb中的處理,首先獲取iframe中body的內容並賦值給響應對象的responseText屬性,然后觸發此次請求的完成事件。此時的頁面效果如下圖所示:

image

 

7. 至此,完成了文件上傳的AJAX效果。

 

 

小結

文件上傳控件用起來方便,不過內部實現卻不是一帆風順的,這也是它姍姍來遲的原因之一。不過這也是FineUI控件的目標,內部實現可以非常復雜,但是對開發人員的接口一定要盡量簡單。

下一篇文章我們會仔細考察下拉列表在FineUI中的用法。

 

注:《FineUI秘密花園》系列文章由三生石上原創,博客園首發,轉載請注明出處。文章目錄 官方論壇


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM