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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM