html5+js拖拽上傳的那些事
最近搞了個拖拽上傳的專案,以為挺簡單,做了之後發現裡面涉及的東西還是非常多的。這裡隨性的談談其中有意思的地方吧。
拖拽事件
-原生拖拽事件
DragEvent上傳是html5的東東,對應的幾個事件有
- drag
- dragend
- dragenter
- dragexit
- dragleave
- dragover
- dragstart
- drop
其中常用的有四個:dragenter、dragover、drop、dragend
分別對應的事件為:拖進、拖來拖去、釋放滑鼠、拖出
換成程式碼就長這樣:
document.addEventListener("dragenter", function ( event ) {
...
}, false);
document.addEventListener("dragover", function( event ) {
...
}, false);
document.addEventListener("drop", function( event ) {
event.preventDefault();//禁止瀏覽器預設行為
...
return false;//禁止瀏覽器預設行為
}, false);
document.addEventListener("dragend", function( event ) {
...
}, false);
一般來說,上傳在”drop”的時候進行,即在document裡鬆開滑鼠,然後可以從event裡面拿到dataTransfer.files這個物件,獲得使用者拖拽的資料。
-jquery封裝的拖拽事件
jquery裡面的拖拽和原生的有一點點區別,然後附帶一個小坑。
$("body").on({
drop:function(e){ //拖後放
e.preventDefault();
//jquery的file要去e.originalEvent裡面拿
var files = e.originalEvent.dataTransfer.files;
...
}
})
似乎是因為在設計時沒有封裝進去,所以用jquery繫結的drop裡的檔案要去event的原本event物件——originalEvent裡面才能拿到。
啥牛逼的工具都是建立在原生api上的,在這裡還是感嘆一句,基礎知識要學好。
-相容性
既然是html5的api,對瀏覽器也會有一定的要求。
針對瀏覽器的最低相容性,mdn上給出的資料如下:
chrome | firefox(Gecko) | ie | opera | safari(webkit) |
---|---|---|---|---|
4 | 3.5(1.9.1) | 10 | 12 | 3.1 |
遍歷檔案
-FileList檔案
事實上,前文有提到過,上傳的檔案儲存在e.dataTransfer.files中。如果察看它的型別的話會發現這是一個FileList型別。
Object.prototype.toString.call(e.dataTransfer.files);
//"[object FileList]"
這種型別專門存放由<input type="file">
上傳的資料。
FileList自帶一個length屬性,以及一個item()方法。它的結構是類似這樣的:
dataTransfer.files:{
0: File,
length: 1
}
乍一看下挺像一個普通的物件,不過和一般的物件有些區別。訪問其中的資料可以使用dataTransfer.files[0]或者dataTransfer.files.item(0)。如果使用for in遍歷的話在不同瀏覽器裡會有意想不到問題。
-遍歷FileList檔案
在此之前,我一直認為dataTransfer.files是一個普通的物件。所以一直使用for in來處理。然而在不同瀏覽器裡有不同的問題。
- 在chrome中使用hasOwnProperty這個方法能夠很好地識別出來那些File檔案,沒有什麼問題。(在這裡不得不感慨chrome的v8核心真是強大。如果大家都用chrome就省了不知多少事。)
- 在ie/edge瀏覽器中hasOwnProperty並不會把[0],[1]這些判斷為自己的屬性。
- 在搜狗瀏覽器中,hasOwnProperty會把length當做自己的屬性。
正確的遍歷應該是這樣的:
var file;
var files = e.dataTransfer.files;
for(var i = 0; i < files.length; i++){
file = files [i];
//或者
file = files.item(i);
alert(file.name);
}
不支援資料夾上傳
由於專案只支援檔案上傳,不支援資料夾上傳,因此需要識別出來拖拽的檔案是否為資料夾。
-檔案屬性?
從e.dataTransfer裡面並不能很好的區分是否拖拽的檔案為資料夾。有這麼幾個原因:
- 部分資料夾在e.dataTransfer.files裡面仍然會顯示size
- 資料夾的File裡type為”“,但是不加拓展名的檔案File裡type也為”“
- 在e.dataTransfer裡沒有找到辨別是否為資料夾的函式
因此沒有什麼直接的方法去判斷。
-FileReader
FileReader是html5的api,用來讀取File檔案。
它提供了三個讀取的方法:
readAsBinaryString | readAsDataURL | readAsText |
---|---|---|
二進位制讀取 | 讀取base64 | 用(指定編碼)文字讀取 |
使用方法如下
var fr = new FileReader();
fr.readAsBinaryString(file);
//fr.readAsDataURL(file);
//fr.readAsText(file);
fr.onload=function(e){
var data = this.result;
}
fr.onerror=function(e){
//...
}
這三個方法都可以用不同形式讀取檔案的內容,但是讀取資料夾的時候會觸發error。因此可以用這個特點去判斷上傳的檔案是否是資料夾。
myFileReader(file,function(result,file){
if(result){
//檔案
}else{
//資料夾
}
});
function myFileReader(file, callback){
if(!window.FileReader){
callback(true,file);
return false;
}
var fr = new FileReader();
fr.readAsDataURL(file);
fr.onload=function(e){
callback(true,file);
}
fr.onerror=function(e){
callback(false,file);
}
return true;
};
另外,檔案越大,讀取的速度越慢。一般來說,如果拖拽的是資料夾,其File裡面的size屬性大小不會超過5M。因此可以先用這個屬性刷掉一波非資料夾,增加預處理的速度。