最近搞了个拖拽上传的项目,以为挺简单,做了之后发现里面涉及的东西还是非常多的。这里随性的谈谈其中有意思的地方吧。
拖拽事件
-原生拖拽事件
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。因此可以先用这个属性刷掉一波非文件夹,增加预处理的速度。