zoukankan      html  css  js  c++  java
  • 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.callapply通常用于动态改变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的特性。这个方法完成的事情包括:

    1. 判断队列中是否有重复文件,有则移除重复
    2. 创建上传进度信息显示,并绑定取消上传的点击事件
    3. 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方法.

    附粗陋流程图:
    uploaifive

  • 相关阅读:
    23
    关系数据库范式
    组合
    排列
    bfs_迷宫求最短路径
    dfs-求连通块
    dfs_部分和问题
    线程
    http://m.blog.csdn.net/article/details?id=51699295
    jquery 页面多个倒计时
  • 原文地址:https://www.cnblogs.com/skyfynn/p/5895583.html
Copyright © 2011-2022 走看看