概述
文件上傳是一個很常見的需求,實現文件上傳的技術也很多。下面就談談一些常見的上傳技術以及它們的優劣。
傳統表單上傳
傳統表單文件上傳估計是運用最廣泛和最簡單的技術了,說它簡單是因為只要指定表單的enctype為multipart/form-data,就行了。簡單可靠所以被運用的廣泛。傳統表單上傳示例如下所示:
<form action="test.php" target="" method="post" enctype="multipart/form-data"> <input type="file" name="file" id="file" /> <input type="submit" id="J_submit" value="submit" /> </form>
表單中的action
參數指的是后台處理上傳文件的接口。target
參數規定了規定在何處打開 action URL,常見的參數有_blank、_self、__top或者指定的iframe。method
參數規定了表單提交的方式,這里只能使用**post**方式提交,而不能用**get**方式。最后enctype
的參數規定了在發送到服務器之前應該如何對表單數據進行編碼方式,常見的參數有**application/x-www-form-urlencoded**、**text/plain**和示例中的**multipart/form-data**,因為上傳的文件都是非純文本傳輸,所以指定的類型必須只能是**multipart/form-data**。
這種方式的局限性是不能批量處理,而且表單上傳是同步的,表單已提交,頁面就會刷新。
第三方插件處理文件上傳
如果你想實現異步提交文件,且可以進行批量處理文件,那么只能通過第三方插件來實現了,第三方插件實現技術有很多,比如Flash、java applet、ActiveX等技術,其中Flash技術是運用最廣泛、最成熟的一種方案。
不過第三方插件已經不屬於前端開發范圍了,所以不會詳細細說。不過到時可以我常用的FLash上傳插件有SWFupload、uploadfile、百度的webupload等。
第三方插件的好處是能做批量處理、異步提交。缺點也是顯而易見的是要瀏覽器支持。
模擬異步上傳文件
說到異步,可能有人會說,能不能通過ajax來實現文件的異步上傳呢?想法很美好,現實卻很殘酷,ajax與后端通信 只能傳送字符串,是無法傳遞實體文件的,所以用ajax是無法實現直接文件上傳的。不過我們卻可以在頁面“埋”一個隱藏iframe
來模擬文本的異步提交。
具體的原理是,在點擊提交按鈕時,動態的生成一個隱藏iframe加入到頁面上,並且 將form 的 target指向該隱藏iframe,服務端就接收到上傳的file文件,並進行相應的操作,然后將返回結果返回到隱藏的iframe里面,這時我們可以與后端開發約定返回結果的格式,可以是json格式,便於我們前端操作,然后前端部分就可以用iframe.contentWindow.document.body.innerHTML來獲取后端返回的結果,進行相應的parseJSON處理,即可像處理ajax返回的json一樣,處理數據。
示例代碼如下所示:
/** * 模擬ajax無刷新文件上傳 */ var fileUpLoad = function(config) { var ifr = null, fm = null, defConfig = { submitBtn: $('#J_submit'), //提交按鈕 complete: function(response) {}, //上傳成功后回調 beforeUpLoad: function() {}, //點擊提交未上傳時回調 afterUpLoad: function() {} //點擊提交上傳后回調 }; //靜態變量 var IFRAME_NAME = 'fileUpLoadIframe'; //配置 config = $.extend(defConfig, config); //綁定submit事件 config.submitBtn.bind('click', function(e){ e.preventDefault(); //點擊提交前觸發事件, 函數返回false可阻止提交表單,用於file為空判斷 if (config.beforeUpLoad.call(this) === false) { return; } //生成一個隱藏iframe,並設置form的target為該iframe,模擬ajax效果 ifr = $('<iframe name="'+ IFRAME_NAME +'" id="'+ IFRAME_NAME +'" style="display:none;"></iframe>'); fm = this.form; ifr.appendTo($('body')); fm.target = IFRAME_NAME; //target目標設為ifr //上傳完畢iframe onload事件 ifr.load(function(){ var response = this.contentWindow.document.body.innerHTML; config.complete.call(this, response); ifr.remove(); ifr = null; //清除引用 }); fm.submit(); //提交表單 //點擊提交后觸發事件 config.afterUpLoad.call(this); }); };
調用方式如下:
fileUpLoad({ submitBtn: $('#J_submit'), complete: function(response){ //上傳成功后處理回調 var d = $.parseJSON(response); alert('返回成功') console.log(d); }, beforeUpLoad: function() { alert('上傳前'); }, afterUpLoad: function() { alert('上傳后'); } });
這種方式的好處是,雖然不是異步提交,但是給人的感覺好像是通過異步方式上傳了文件。缺點是依然不能進行批量處理文件。
使用FormData對象發送文件
XMLHttpRequest Level 2添加了一個新的接口FormData.利用FormData對象,我們可以通過JavaScript用一些鍵值對來模擬一系列表單控件,我們還可以使用XMLHttpRequest的send()方法來異步的提交這個"表單".比起普通的ajax,使用FormData的最大優點就是我們可以異步上傳一個二進制文件.其兼容性如下所示:
有圖可知,這個接口兼容性在IE上表現的並不是很好,最新只支持IE10+,不過IE10+的市場份額現在還不是很多,如果你考慮兼容性的話,建議不要使用這個接口。
那怎樣通過FormData
上傳文件呢。可以參考下面的代碼。
html結構:
<form enctype="multipart/form-data" method="post" name="fileinfo"> <label>Your email address:</label> <input type="email" autocomplete="on" autofocus name="userid" placeholder="email" required size="32" maxlength="64" /><br /> <label>Custom file label:</label> <input type="text" name="filelabel" size="12" maxlength="32" /><br /> <label>File to stash:</label> <input type="file" name="file" required /> </form> <div id="output"></div> <a href="javascript:sendForm()">Stash the file!</a>
腳本代碼:
function sendForm() { var oOutput = document.getElementById("output"); var oData = new FormData(document.forms.namedItem("fileinfo")); oData.append("CustomField", "This is some extra data"); var oReq = new XMLHttpRequest(); oReq.open("POST", "stash.php", true); oReq.onload = function(oEvent) { if (oReq.status == 200) { oOutput.innerHTML = "Uploaded!"; } else { oOutput.innerHTML = "Error " + oReq.status + " occurred uploading your file.<br \/>"; } }; oReq.send(oData); }