打造 html5 文件上傳組件,實現進度顯示及拖拽上傳,支持秒傳+分片上傳+斷點續傳,兼容IE6+及其它標准瀏覽器


老早就注冊了博客園帳號,昨天才發現,連博客都沒開,Github也是一樣,深覺慚愧,趕緊潛個水壓壓驚`(*∩_∩*)′

言歸正傳。大概許多人都會用到文件上傳的功能,上傳的庫貌似也不少,比如(jQuery File UploaderFineUploaderUploadifyBaidu 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 拖拽與拖放簡介

代碼下載

asp.net 或其它后台示例代碼

Node.js 示例代碼

寫在最后

如果本文或本項目對您有幫助的話,請不吝點個贊。歡迎交流!


免責聲明!

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



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