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]"

这种类型专门存放由上传的数据。

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。因此可以先用这个属性刷掉一波非文件夹,增加预处理的速度。

你可能感兴趣的:(上传下载)