頁面里經常要用到文件上傳的功能,而且要求頁面不刷新,先說一下原理:頁面里放一個file控制項和submit按鈕,外面用form表單包住,給form表單加上對應的屬性值,action、method、entype、name,到這一步,能上傳文件了,但是這樣上傳文件會刷新頁面,這不是我們想要的。我們要的是文 ...
頁面里經常要用到文件上傳的功能,而且要求頁面不刷新,先說一下原理:頁面里放一個file控制項和submit按鈕,外面用form表單包住,給form表單加上對應的屬性值,action、method、entype、name,到這一步,能上傳文件了,但是這樣上傳文件會刷新頁面,這不是我們想要的。我們要的是文件上傳時不刷新頁面,那麼也簡單,在頁面里放一個iframe,設置它的寬高為0,這裡有兩個坑:
1、需要設置iframe的name值與form的target屬性值一樣,意思就是把form表單上傳文件的刷新轉嫁到iframe里去了;
2、form表單的enctype屬性值必須設置成multipart/form-data,將文件轉換成文件流供後端接收;
代碼如下:
<iframe name="fileUpload"></iframe> <form method="post" action="xxxx" enctype="multipart/form-data" name="fileForm" target="fileUpload"> <input type="file" class="fileInput" name="fileInput"> <input type="submit" value="提交" /> </form>
頁面(這裡為了看到效果,就不將iframe的寬高設為0了):
事情就這麼愉快地結束了嗎?當然沒有,離國慶節還有那麼些天,不要著急。
到這裡文件能上傳了,頁面也不會刷新,那麼還差什麼?當然是精益求精--優化啦。怎麼優化?假如頁面里有三個地方需要上傳不同類型的文件,最好的辦法肯定不是在頁面里將代碼copy三份,然後就這樣用,這是普通開發的做法,我們可以利用js動態生成上面這些代碼,需要上傳文件的地方,一個函數加參數就搞定了,代碼如下:
![複製代碼](http://common.cnblogs.com/images/copycode.gif)
/*2014年9月18日17:39:47 By 王美建*/ function ajaxUpload(opt){ /* 參數說明: opt.frameName : iframe的name值; opt.url : 文件要提交到的地址; opt.fileName : file控制項的name; opt.format : 文件格式,以數組的形式傳遞,如['jpg','png','gif','bmp']; opt.callBack : 上傳成功後回調; */ var iName=opt.frameName; //太長了,變短點 var iframe,form; //創建iframe和form表單 iframe = $('<iframe name="'+iName+'" />'); form = $('<form method="post" style="display:none;" target="'+iName+'" action="'+opt.url+'" name="form_'+iName+'" enctype="multipart/form-data" />'); file = $('<input type="file" name="'+opt.fileName+'" />'); file.appendTo(form); //插入body $(document.body).append(iframe).append(form); //觸發瀏覽事件,選擇文件 file.click(); //選中文件後,驗證文件格式是否符合要求 file.change(function(){ //取得所選文件的擴展名 var fileFormat=$(this).val().exec(/\.[a-zA-Z]+$/)[0].substring(1); if(opt.format.join('-').indexOf(fileVal)!=-1){ form.submit();//格式通過驗證後提交表單; }else{ iframe.remove(); form.remove(); alert('文件格式錯誤,請重新選擇!'); } }); //文件提交完後 iframe.load(function(){ var data = $(this).contents().find('body').html(); opt.callBack(data); iframe.remove(); form.remove(); }) }
![複製代碼](http://common.cnblogs.com/images/copycode.gif)
使用方法:在頁面里放一個按鈕Btn,點擊Btn時觸發ajaxUpload方法,ajaxUpload方法內部自動創建上傳所需要的元素並自動觸發file.click()事件供用戶選擇文件,選中文件後自動驗證文件格式並提交,然後返回後端返回的結果,到這裡,問題解決了80%,為什麼不是100%?ajaxUpload方法在IE8以上及火狐、chrome瀏覽器都沒有問題,但在IE8及以下的瀏覽器上傳文件會提示:沒有許可權!這是因為低版本的IE做了安全限制,file控制項必須由用戶主動點擊觸發選擇的文件才可以上傳,而不能使用js的click事件來模擬點擊觸發。在此我又想說,IE我~!@#¥%……&*()——……。
辦法總比困難多,既然一定要由用戶點擊來觸發,那麼直接把頁面里的按鈕替換成file控制項吧,iframe和form還是動態創建,當用戶點擊file控制項選擇文件後,會觸發file控制項的chang事件,給file控制項的change事件綁定ajaxUpload方法並將file控制項的id傳進去,ajaxUpload方法通過id獲取file控制項並將file控制項appendTo到動態創建的form里,之後的步驟與上面無異——驗證格式→提交表單→觸發回調。細心的同學會發現,選擇文件後,file控制項會appendTo到form表單里,那頁面里放file控制項的地方不是空了麽?並且,表單提交後,file會隨著form一起被remove掉,所以,在file控制項appendTo到form前,先建一個變數P將file控制項的父級存起來,form表單提交之後,先將file控制項appendTo回到P裡面,當然,file控制項appendTo到form時,file控制項依然會在頁面里消失,所以頁面里的Btn要保留,把file控制項定位在Btn上面,透明度設置成0,這樣點擊Btn實際上點擊的是蓋在上面的file控制項,這樣即使file控制項被appendTo到form裡面,用戶也不會察覺到什麼變化,問題迎刃而解!相容的寫法如下:
![複製代碼](http://common.cnblogs.com/images/copycode.gif)
/*2014年9月19日11:11:07 By 王美建*/
function ajaxUpload(opt){ /* 參數說明: opt.id : 頁面里file控制項的ID; opt.frameName : iframe的name值; opt.url : 文件要提交到的地址; opt.format : 文件格式,以數組的形式傳遞,如['jpg','png','gif','bmp']; opt.callBack : 上傳成功後回調; */ var iName=opt.frameName; //太長了,變短點 var iframe,form,file,fileParent; //創建iframe和form表單 iframe = $('<iframe name="'+iName+'" />'); form = $('<form method="post" style="display:n1one;" target="'+iName+'" action="'+opt.url+'" name="form_'+iName+'" enctype="multipart/form-data" />'); file = $('#'+opt.id); //通過id獲取flie控制項 fileParent = file.parent(); //存父級 file.appendTo(form); //插入body $(document.body).append(iframe).append(form); //取得所選文件的擴展名 var fileFormat=/\.[a-zA-Z]+$/.exec(file.val())[0].substring(1); if(opt.format.join('-').indexOf(fileFormat)!=-1){ form.submit();//格式通過驗證後提交表單; }else{ file.appendTo(fileParent); //將file控制項放回到頁面 iframe.remove(); form.remove(); alert('文件格式錯誤,請重新選擇!'); }; //文件提交完後 iframe.load(function(){ var data = $(this).contents().find('body').html(); file.appendTo(fileParent); iframe.remove(); form.remove(); opt.callBack(data); }) }
![複製代碼](http://common.cnblogs.com/images/copycode.gif)
國際慣例,到這裡,方法已經接近完美了,為什麼是接近?來看一張圖片:
結構代碼:
.fileInput{ position: absolute;left: 0;top: 0;height: 30px; filter:alpha(opacity=60);opacity:0.6; background-color: transparent;} .btn{width: 200px;height: 30px; margin: 100px auto; background-color: yellow; text-align: center; line-height: 30px; overflow: hidden; display: block; position: relative;} <div class="btn">選擇文件<input type="file" class="fileInput" name="fileInput"></div>
這就是放在頁面里的file控制項,外面用一個div包住,在IE10及以下瀏覽器中,用戶單擊紅色框部分是不會彈出文件選擇框的,必須單擊藍色部分或雙擊紅色部分才行,要讓藍色部分占滿外面的div怎麼做到呢?用css設置寬度只會增加紅色部分的寬度,這時我們會發現藍色部分是有字的,對,可以通過設置font-size來使藍色部分變寬,然後給file控制項加上dir="rtl",這會讓瀏覽按鈕移到左邊,再給.btn加上overflow:hidden,可以發現瀏覽按鈕已經占滿整個div了,如下圖:
到這裡才真正完成了100%,最後,我們還可以給file控制項設置accept屬性,限制可選文件格式(IE8及以下不支持該屬性),別忘了把file控制項的透明的改為0。