老早就注冊了博客園帳號,昨天才發現,連博客都沒開,Github也是一樣,深覺慚愧,趕緊潛個水壓壓驚`(*∩_∩*)′
言歸正傳。大概許多人都會用到文件上傳的功能,上傳的庫貌似也不少,比如(jQuery File Uploader、FineUploader、Uploadify、Baidu Web Uploader 等等),功能都很強大,代碼量一般也較大。當時心想,就這么個小功能,殺雞焉用牛刀,用庫的話還得熟悉它的用法,有的需要引入額外的庫,純Flash的不考慮,還是動手造個輪子得了,至少造過之后能知道底層的原理,關鍵是,自己寫的東西,維護和修改方便,對過程可以進行全方位的控制。造個輪子簡單,造個好用的輪子卻就不是那么簡單了。上傳功能很快完成,但下次又用到的時候,卻發現拿過來用需要修改不少東西,一是使用的JS庫發生了變化,再就是UI界面完全不同,當時將上傳邏輯和界面寫的死死的,遇到不同的情況就需要針對性的改造。針對單一情況寫的東西當然最輕量,只用考慮當前情況,不用的功能可以統統拋開,能做到最精簡,但不方便重用。一個輪子如果不能重用,那當然不能算一個合格的輪子。意識到這點,而且既然已經走上了造輪子這條路,需要的核心功能也沒有大的變化,那還是走下去吧。然后一路修修改改,最后完成了一個可用的版本。
v1.4版本已支持秒傳+分片上傳+斷點續傳(IE10+、其它標准瀏覽器),具體請參考Github代碼。
關於我的上傳控件
特點:
- 輕量級,不依賴任何JS庫,核心代碼(Q.Uploader.js)僅約700行,min版本加起來不到12KB
- 純JS代碼,無需Flash,無需更改后台代碼即可實現帶進度條(IE10+、其它標准瀏覽器)的上傳,其它(eg:IE6+)自動降級為傳統方式上傳
- 上傳核心與UI界面分離,可以很方便的定制上傳界面包括上傳按鈕
- 上傳文件的同時可以指定上傳參數,支持上傳類型過濾
- 完善的事件回調,可針對上傳的每個過程進行單獨處理
- 方便的UI接口,上傳界面可以隨心所欲的定制


簡單調用示例(暴露的全局對象為Q):
var uploader = new Q.Uploader({ url: "api/upload.ashx?type=file", target: document.getElementById("upload-target"), view: document.getElementById("upload-view"), //html5: true, //是否啟用html5上傳,默認為true //multiple: true, //是否允許多選(僅html5模式有效),默認為true //auto: true, //添加任務后是否立即上傳,默認為true //允許或不允許上傳的文件類型 (allows 與 disallows 可單一或組合使用) //allows: ".txt,.jpg,.png,.gif,.zip,.rar,.7z", //disallows:".exe" //每次上傳都會發送的參數(POST方式) data: { user: "Devin" } });
詳細見示例代碼或 Github(感覺逼格瞬間提升了有木有,*^_^* ),示例代碼和源碼在文章末尾
https://github.com/devin87/web-uploader
原理
1. html5上傳
這個需要瀏覽器支持,無論是html5上傳還是和html4上傳,html上傳控件都是必不可少的,即:
<input type="file">
//監聽 input 的 change 事件,獲取要上傳的數據 input.onchange = function () { //input.files 屬性需要瀏覽器支持 var files = this.files; for (var i = 0, len = files.length; i < len; i++) { var file = files[i]; //file.name || file.fileName => 文件名稱 //file.size || file.fileSize => 文件大小 upload_html5(file); } };
//html5 上傳 var xhr = new XMLHttpRequest(); //上傳進度事件 xhr.upload.addEventListener("progress", function (e) { }, false); //上傳完成(成功)事件 xhr.addEventListener("load", function (e) { //獲取服務器響應 var text = e.target.responseText; }, false); //上傳失敗事件 xhr.addEventListener("error", function (e) { }, false); //上傳中斷(取消)事件 xhr.addEventListener("abort", function (e) { }, false); var fd = new FormData; //添加要上傳的文件對象
fd.append("file", file);
xhr.open("POST", "api/upload.ashx");
xhr.send(fd);
2. html4上傳(傳統上傳)
通過將 form 的 target 指向 iframe 的 name 來實現無刷新上傳
<iframe name="html4-upload-target"></iframe> <form action="api/upload.ashx" method="post" enctype="multipart/form-data" target="html4-upload-target"> <input type="file" name="upfile" /> </form>
//iframe load 事件 //注意:低版本 ie 支持 iframe 的 onload 事件,不過是隱形的(iframe.onload 方式綁定的將不會觸發),需要通過 attachEvent 來注冊 function bind_iframe_load(iframe, fn) { if (iframe.attachEvent) iframe.attachEvent("onload", fn); else iframe.addEventListener("load", fn, false); } //html4上傳完成回調 bind_iframe_load(iframe, function () { //獲取服務器響應 var text = iframe.contentWindow.document.body.innerHTML; });
美化
上傳按鈕很丑,我想美化咋整?IE10+及標准瀏覽器支持 input.click() 觸發文件選擇,有人說,低版本IE也能彈出這個框框呀,這里有個坑,彈是能彈,但是可能基於安全考慮無法獲取文件數據,報拒絕訪問。
既然必須點擊上傳控件來選取文件,那么可以通過絕對定位讓上傳控件遮蓋上傳按鈕,然后將上傳控件透明度設為0,以達到隱身的效果,如此一來表面點擊的是上傳按鈕,實際點擊的是上傳控件,問題也就隨之解決。
<!-- 上傳按鈕,樣式隨自己控制,假設寬高為分別為120px,36px,相對於body的邊距分別為100px,100px --> <a class="upload-target">選擇文件上傳</a> <!-- 上傳控件,選擇文件后(change事件)將 input 追加到 form 即可 --> <div style="width:120px;height:36px;position:absolute;left:100px;top:100px;overflow:hidden;filter: alpha(opacity=0);opacity:0;"> <input type="file" name="upfile" style="width:120px;height:36px;font-size:100px;" /> </div>
關於html4上傳,剛好看到一篇 利用iframe無刷新上傳文件的坑 文章,有興趣的童鞋可以看看。
拖拽文件上傳
文件拖拽利用了 HTML5 中的 Drag and Drop 特性,可以直接將本地電腦上的文件拖拽到網頁中。
1. 定義拖拽區域
<div id="drop-area">將文件拖拽到此區域</div>
2. 獲取拖拽數據並上傳
var boxDropArea = $("#drop-area")[0]; //TODO:檢測支持情況 //阻止瀏覽器默認拖放行為 $(boxDropArea).on("dragleave dragenter drop dragover", function (e) { e.preventDefault(); e.stopPropagation(); }); //獲取拖拽的文件對象並上傳 $(boxDropArea).on("drop", function (e) { var files = e.dataTransfer.files; if (!files || files.length == 0) return; for (var file in files) { //TODO:上傳文件 upload(file); } });
想了解更多拖拽的信息,可以參考這篇文章 HTML5 drag & drop 拖拽與拖放簡介
代碼下載
寫在最后
如果本文或本項目對您有幫助的話,請不吝點個贊。歡迎交流!
