uploadifive源码学习
一、简介
Uploadify是一个基于JQuery的多文件上传JS组件,高度定制,两个版本可供选择。flash版本在最新的Safari等不再支持flash的浏览器或者一些手机浏览器中就无法正常的加载使用,因此推荐使用html5版本!
最近,想了解JavaScript的编码规范,所以根据用过的Uploadifive作为参考,看如何组织js代码以及如果开发jquery的扩展。
二、代码结构概览
如果从第一行开始读代码,会摸不着头脑,所以先看一下他的整体布局。
(function($) {
var methods = {
//所有上传用到的方法和属性
init:function(options){},
debug : function() {},
clearQueue : function() {}//清理队列
cancel : fucntion(file, fast){},//取消文件上传
upload : function(file, keepVars) {},
destroy : function() {},
}
$.fn.uploadifive = function(method) {
if (methods[method]) {
return methods[method].apply(this,Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('The method ' + method + ' does not exist in $.uploadify');
}
}
})(jQuery);
知识点:
1.$.extend
为类级别的扩展,如:
//扩展
$.extend({
req:function(url){return true}
})
//使用
$.req('xxxx')
2.$.fn.extend
为对象级别的扩展,如:
//扩展
$.fn.extend({
validate:function(){return true}
})
//使用
$("#id").validate();
3.call
与apply
通常用于动态改变this,调用其他对象的方法,区别在于call参数逐个传递,apply将所有参数放入数组(arguments)行传递
三、解析
1.首先扩展中对传入的method
参数进行判断,如果存在这个方法就直接调用相应的methods对象中的方法,否则如果是个对象,就调用methods的init
初始化方法。
2.init 方法
var $this = $(this);//当前被绑定的input的context object
$this.data('uploadifive', {
// 这里初始化的是文件上传队列、多文件上传、文件数量等记录用的
//属性
});
//暂存uploadifive 并保存在$data中
var $data = $this.data('uploadifive');
//将用户绑定上传组件传入的参数与默认的参数进行合并
var settings = $data.settings = $.extend({},options);
//计算用户传入的配置参数fileSizeLimit
if (isNaN(settings.fileSizeLimit)) {
//如果有kb等单位,这里根据用户输入的是KB MB GB进行文件大小计算
} else {
//否则以kb为单位
settings.fileSizeLimit = settings.fileSizeLimit * 1024;
}
//创建type为file的input模板保存为$data的一个属性,这个模板会在后面取代用户页面原有的input 作为一个可点击的透明层存在
$data.inputTemplate = $('<input type="file">')
.css({
'font-size' : settings.height + 'px',
'opacity' : 0,
'position' : 'absolute',
'right' : '-3px',
'top' : '-3px',
'z-index' : 999
});
//接下来初始化一系列方法绑定到$data上
//创建一个新的input 会用到上面的inputTemplate
$data.createInput = function(){...}
//清除一input
$data.destroyInput = function(){...}
//拖动上传方法
$data.drop = function(e) {...}
//检查文件在队列中是否存在方法
$data.fileExistsInQueue = function(file) {...}
//删除队列中一个已经存在文件
$data.removeExistingFile = function(file) {...}
//接下来初始化创建itemTemplate模板,这个模板就是开始上传时,会有一个进度 条以及一个x点击取消按钮,保存至$data.queueItem中
if (settings.itemTemplate == false) {
$data.queueItem = $('模板div代码');
} else {
$data.queueItem = $(settings.itemTemplate);
}
//添加文件到文件队列中的方法初始化
$data.addQueueItem = function(file) {...}
//上传队列中移除其中一个,多文件上传时候用到
$data.removeQueueItem = function(file, instant, delay) {...}
//计算待上传文件的数量方法
$data.filesToUpload = function() {...}
//检查文件是否存在的方法
$data.checkExists = function(file){...}
//真正实现文件流上传的方法
$data.uploadFile = function(file, uploadAll) {...}
//更新文件上传进度的方法
$data.progress = function(e, file) {...}
//错误信息触发
$data.error = function(errorType, file, uploadAll) {}
//文件上传完成后触发的方法
$data.uploadComplete = function(e, file, uploadAll) {}
//全部文件上传完成触发的方法
$data.queueComplete = function() {}
截止到现在,上面的代码都是方法的定义,模板的定义全部保存在$data中,页面中还没有任何的效果,接下来开始执行一些列跟页面文件上传展现相关初始化操作。
//判断是否支持HTML5
if (window.File && window.FileList && window.Blob && (window.FileReader || window.FormData)) {
下面这部分的操作就是创建一个按钮,设置基本的按钮的样式,如果用户传入了自定 义的设置参数,则相应的赋值。按钮其实是在透明的input file上的一个div
settings.id = 'uploadifive-' + $this.attr('id');
// Wrap the file input in a div with overflow set to hidden
$data.button = $('<div id="' + settings.id + '" class="uploadifive-button">' + settings.buttonText + '</div>');
if (settings.buttonClass) $data.button.addClass(settings.buttonClass);
// Style the button wrapper
$data.button.css({
'height' : settings.height,
'line-height' : settings.height + 'px',
'overflow' : 'hidden',
'position' : 'relative',
'text-align' : 'center',
'width' : settings.width
});
// Insert the button above the file input
$this.before($data.button)
// Add the file input to the button
.appendTo($data.button) //将原有的input type=file 插入到div中并display none
// Modify the styles of the file input
.hide();
}
到现在按钮可以显示在页面上了,但是上传的操作还没有初始化、绑定。接下来调用上面已经初始化好的createInput方法,创建一个新的input type file以便添加用户的一些配置信息,如允许的文件类型,是否多文件上传等。
$data.createInput.call($this);
createInput方法内容:
$data.createInput = function() {
//获取之前初始化好的保存在$data中的input type file模板
var input = $data.inputTemplate.clone();
//设置input name multip accept等属性
...
//对创建的input绑定change事件
input.bind('change', function() {
//初始化队列记录参数
...
//判断是否超过了设置的上传队列数量限制,超过了则回调报错,否则进行添加入队列处理
if(超过了限制){
onError();
}else{
//遍历files逐个添加到上传队列
for (var n = 0; n < limit; n++) {
file = this.files[n];
$data.addQueueItem(file);
}
$data.inputs[inputName] = this;
$data.createInput();//重新创建input并绑定change事件
}
}
//将绑定好事件的input插入到之前创建的button 就是div层里面
$data.button.append(input);
}
然后代码回到$data.createInput.call($this);
的调用位置接着往下走,可以看到有一个是否设置了queueID的判断,就是如果设置了queueID就直接创建一个div容器,这里面就是上传时,上传状态进度等信息出现的地方,如果没有,则默认在上传按钮下面创建一个有默认id和class的容器,暂时保存在$data.queueEl
中。
if (!settings.queueID) {
settings.queueID = settings.id + '-queue';
$data.queueEl = $('<div id="' + settings.queueID + '" class="uploadifive-queue" />');
$data.button.after($data.queueEl);
} else {
$data.queueEl = $('#' + settings.queueID);
}
uploadifive还支持拖动上传,下面的代码是绑定拖动上传的监听事件
if (settings.dnd) {
//dragleave
//dragenter
//dragover
//drop
}
初始化操作已经完成,等待上传的触发!
当点击选择图片->选择一个文件->确定上传,则触发了createInput中绑定的change事件,这里是一个过渡,只是做了队列文件数量的验证,然后遍历文件对象调用$data.addQueueItem(file)
.
addQueueItem,添加file对象到队列中,事实上多文件上传是html5支持的,所以并不是uploadifive的特性。这个方法完成的事情包括:
- 判断队列中是否有重复文件,有则移除重复
- 创建上传进度信息显示,并绑定取消上传的点击事件
- file.queueItem.data('file', file); 暂存文件流,在后面真正的文件上传时候要获取文件流
if(onAddQueueItem方法 没有被覆盖){
完成上面所述的1,2,3操作
}
//如果用户绑定时有onAddQueueItem则触发回调
...
//文件大小判断
...
执行完暂存的加入队列的操作,回到createInput的绑定change事件内继续执行,如果配置了自动上传,调用upload方法。
if (settings.auto) {
methods.upload.call($this);
}
下面进入upload方法。upload方法首先拿到所有uploadifive的配置信息,然后判断是否直接传入了file文件流,是则调用真正的上传方法uploadFile
if (file) {
$data.uploadFile.call($this, file);
} else {
if(没有超过上传文件文件数量限制){
//循环上传队列中没有错误和没有完成的文件
$('#' + settings.queueID).find('.uploadifive-queue-item').not('.error, .complete').each(function() {
/**
* $(this)代表的是当前的上传队列的对象,而在addQueeuItem中 有
* 这样一句 file.queueItem.data('file', file);就是缓存了数据
* 所以这里可以直接访问到暂存数据
*/
_file = $(this).data('file');
// Check if the simUpload limit was reached
//是否配置有重复文件检查脚本链接checkScript参数,则check,否则调用真正的uploadFile方法
if (settings.checkScript) {
_file.checking = true;
skipFile = $data.checkExists(_file);
_file.checking = false;
if (!skipFile) {
$data.uploadFile(_file, true);
}
} else {
$data.uploadFile(_file, true);
}
});
if (队列中文件都上传完成) {
$data.queueComplete();
}
}
}
uploadFile中使用了两种上传方式,如果支持FormData则使用异步的ajax方式上传,否则使用FileReader以二进制流的方式上传。这种文件上传的方式还是值得学习一下的。
//创建XMLHttpRequest对象
xhr = file.xhr = new XMLHttpRequest();
if(支持FormData){
//创建表单
xhr.open(settings.method, settings.uploadScript, true);
// On progress function
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
//调用progress方法,progress方法就是计算上传完成百分比
$data.progress(e, file);
}
}, false);
}else{
//以二进制流方法上传
var reader = new FileReader();
...
}
最后上传完成调用queueComplete
方法,整个流程走完了。
四、总结
简化版的执行流程是:
绑定input,定制参数->初始化上传方法、属性->createInput创建上传按钮->绑定change事件->文件上传触发change->文件天加到队列调用addQueueItem->上传调用upload->upload必要验证后调用真正的上传方法uploadFile->上传过程中调用progress计算进度->上传完成调用queueComplete方法.
附粗陋流程图:
uploadifive源码学习的更多相关文章
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- jQuery源码学习感想
还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)
前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...
- 我的angularjs源码学习之旅2——依赖注入
依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...
- ddms(基于 Express 的表单管理系统)源码学习
ddms是基于express的一个表单管理系统,今天抽时间看了下它的代码,其实算不上源码学习,只是对它其中一些小的开发技巧做一些记录,希望以后在项目开发中能够实践下. 数据层封装 模块只对外暴露mod ...
- leveldb源码学习系列
楼主从2014年7月份开始学习<>,由于书籍比较抽象,为了加深思考,同时开始了Google leveldb的源码学习,主要是想学习leveldb的设计思想和Google的C++编程规范.目 ...
随机推荐
- Excel 复制Sql查询结果错位
SELECT TOP 30000 REPLACE(REPLACE(T1.ReceiverName,CHAR(10),' '),CHAR(13),' ') AS ReceiverName, REPLAC ...
- 初始go语言
一.创建第一个go语言程序:打印hello world! package main import "fmt" func main() { fmt.Println("Hel ...
- Hibernate 注解说明
转:http://blog.csdn.net/u012312373/article/details/46566081 1.类级别注解 @Entity 映射实体类 @Table 映 ...
- foreach 循环的应用传值
$arr=array(1,5,8,8,9);foreach ($arr as $key => $value) { //这里可以一边改外面$arr的值一边下一步循环 $value=++$value ...
- C 语言学习准备
摘要:用 C#语言学习了一些数据结构,突然想学习 C 语言,为了学习C,本文准备好资料. C 语言学习准备 作者:乌龙哈里 时间:2015-11-17 平台:Window7 64bit,Visual ...
- C++中Map常见用法以及按照value排序
今天做一个简单的算法题,居然用了1个小时,STL unordered_map用多了,没想到map这次派上了用场,这里记录一下: 算法题为 给一个字符串例如 abaaba,每连续两个字符组成一个子串 ...
- java复习-多线程
和线程之间的关系: 进程:进程是程序的一次动态执行过程,他经理了代码加载,执行到执行完毕的一个完整过程,这个过程也是进程本身从产生,发展到最终消亡的过程. 线程:线程是实现并发机制的一种有效手段,进程 ...
- 如何在sublime中使用sass
搞了好久,终于把sass搞定了. 最开始,我是想使用koala来实现对sass的实时编译的,但是每当我保存的时候,总是弹出erro错误,即无法编译生成css文件,百度了半天,问了好久,这个问题还是没能 ...
- iOS开发上架之itunes connect里app信息的编辑
sku用于我们在后台识别自己的app,所以随你怎么填写
- 《Mastering Opencv ...读书笔记系列》车牌识别(II)
http://blog.csdn.net/jinshengtao/article/details/17954427 <Mastering Opencv ...读书笔记系列>车牌识别(I ...