頁面裡經常要用到檔案上傳的功能,而且要求頁面不重新整理,先說一下原理:頁面裡放一個file控件和submit按鈕,外面用form表單包住,給form表單加上對應的屬性值,action、method、entype、name,到這一步,能上傳檔案了,但是這樣上傳檔案會重新整理頁面,這不是我們想要的。我們要的是檔案上傳時不重新整理頁面,那麼也簡單,在頁面裡放一個iframe,設定它的寬高為0……

頁面裡經常要用到檔案上傳的功能,而且要求頁面不重新整理,先說一下原理:頁面裡放一個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動态生成上面這些代碼,需要上傳檔案的地方,一個函數加參數就搞定了,代碼如下:
/*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();
})
}
使用方法:在頁面裡放一個按鈕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裡面,使用者也不會察覺到什麼變化,問題迎刃而解!相容的寫法如下:
/*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);
})
}
國際慣例,到這裡,方法已經接近完美了,為什麼是接近?來看一張圖檔:
利用iframe無重新整理上傳檔案的坑
結構代碼:
.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了,如下圖:
利用iframe無重新整理上傳檔案的坑
到這裡才真正完成了100%,最後,我們還可以給file控件設定accept屬性,限制可選檔案格式(IE8及以下不支援該屬性),别忘了把file控件的透明的改為0。