1. 程式人生 > >html5+js拖拽上傳的那些事

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。因此可以先用這個屬性刷掉一波非資料夾,增加預處理的速度。