2014-09-10 82 views
0

我開發了一個ajax文件上傳器,並且如果一個實例在頁面上,效果很好。我試圖將它轉換爲允許多個實例,並且它幾乎可以工作,除了當我以一種形式上傳多個文件時,它會上傳第一個文件,但對於所有後續文件,由於某種原因它指向前進到第二個表單實例的下一個文件讀取器。如果我使用上傳表單的最後一個(第二個)實例上傳,則多個文件可以正常上傳。但是,如果我嘗試上載第一個實例,它將上傳第一個文件,但所有後續文件都會發送到第二個實例的空文件輸入。我不明白爲什麼。我在整個上傳表單中使用唯一的id名稱,並在整個javascript函數中引用該唯一id。我會盡量在下面包含所有必要的代碼。Ajax上傳器在同一頁面上的多個實例

表單由PHP生成。 php var $uid是一個隨機生成的唯一ID,我始終使用它。首先,上傳功能的初始化是輸出到頁面的一個<script>標籤:

'<script> '. 
    'jQuery(document).ready(function($){ '. 
     'var myup_'.$uid.' = new MyUp({ '. 
      'form_id: "myup_form_'.$uid.'", '. 
      'uid: '.$uid.', '. 
      'container: "'.$name.'", '. 
      'table: "'.$style.'", '. 
      'iconcolor: "'.$iconcolor.'", '. 
      'maxsize: '.$maxsize.', '. 
      'permitted: '.$permitted.', '. 
      'prohibited: '.$prohibited.', '. 
      'fixed: '.$fixedsetting.', '. 
      'pathcheck: "'.$pathcheck.'", '. 
      'uploader: '.$uploader.', '. 
      'loading: "'.my_url.'/lib/img/ajax.gif" '. 
     }); '. 
    '}); '. 
'</script>'; 

顯然,所有我的設置這些變量前面定義。通過頁面上的多個實例,它可以爲每個不同的實例正確讀取設置。他們成功地顯示了不同的樣式,圖標顏色,文件類型權限,最大文件大小設置等。所有這些都適用於多個實例。

現在的形式:

'<div class="myup_container" style="'.$inlinestyle.'">'. 
    '<form name="myup_form_'.$uid.'" id="myup_form_'.$uid.'" action="javascript:void(0);" enctype="multipart/form-data">'. 
     '<input type="hidden" id="upload-actionpath-'.$uid.'" value="'.$fixed.'" data-basename="'.$basename.'" data-start="'.$start.'" />'. 
     '<div class="myup_buttons_container" style="text-align:right;">'. 
      '<span class="myup_wrapper" style="text-align:left;">'. 
       '<input type="file" name="myup_files_'.$uid.'[]" id="myup_files_'.$uid.'" class="hidden_browse"'.$multiple.' />'. 
       '<span class="add_files">'.$addfiles.'</span>'. 
       '<span id="submit_upload_'.$uid.'">'.$uploadlabel.'</span>'. 
      '</span>'. 
     '</div>'. 
    '</form>'. 
    '<div id="myup_files_container_'.$uid.'" class="myup_files_container"></div>'. 
    '<span id="my_removedfiles_'.$uid.'" style="display:none;"></span>'. 
'</div>'; 

所以這是它的削減版本。腳本標記中包含設置和html表單的實例由shortcode輸出。因此,頁面上的多個簡碼會輸出MyUp功能的多個實例。

這裏是我的希望是所有的JavaScript函數相關的位(這是很長,很抱歉,但我刪除了一噸的東西):

jQuery(document).ready(function($) 
{ 
// throughout, the var fuid will refer 
// to the php var $uid from the html form and instance settings 

function myupRemove(id, filename, fuid) 
{ 
    // handle files the user removes from the input field 
    // before submitting the upload 
} 
function MyUp(config) 
{ 
    this.settings = config; 
    this.fuid = this.settings.uid; 
    this.file = ""; 
    this.browsed_files = []; 
    var self = this; 
    MyUp.prototype.myupDisplay = function(value) 
    { 
     this.file = value; 
     if(this.file.length > 0) 
     { 
      /* this is a really long bit of code 
      * that i'll spare you, but basically I iterate 
      * through all the files in the input field 
      * and put them dynamically into a table 
      * and drop the table onto the page 
      * so the user can rename them, remove them before upload, 
      * and then watch the status bar for each file as it uploads. 
      * This portion of the code works fine with multiple instances. 
      */ 
     } 
    } 
    // Create Unique ID 
    MyUp.prototype.uid = function(name) 
    { 
     // Here I generate a unique ID for each file, 
     // and prepend it with the Instance's unique id (i.e., this.fuid) 
     return this.fuid+'_'+name.replace(/[^a-z0-9\s]/gi, '_').replace(/[_\s]/g, '_'); 
    }   
    // Get File Extension 
    MyUp.prototype.ext = function(file, lowercase) 
    { 
     return (/[.]/.exec(file)) ? (lowercase ? /[^.]+$/.exec(file.toLowerCase()) : /[^.]+$/.exec(file)) : ''; 
    } 
    // Format File Size 
    MyUp.prototype.nicesize = function(fileSize) 
    { 
     // a bunch of code to format the file size then... 
     return niceSize; 
    } 
    // Attribute FileType Icons 
    MyUp.prototype.icon = function(icon_ext, color) 
    { 
     // a bunch of arrays to determine 
     // which file type icon to display in the table 
    } 
    //File Reader 
    MyUp.prototype.read = function(e) 
    { 
     if(e.target.files) 
     { 
      // references the myupDisplay function 
      // where I add the files to a table 
      self.myupDisplay(e.target.files); 
      self.browsed_files.push(e.target.files); 
     } 
    } 
    function addEvent(type, el, fn) 
    { 
     if(window.addEventListener) 
     { 
      el.addEventListener(type, fn, false); 
     } 
     else if(window.attachEvent) 
     { 
      var f = function() 
      { 
       fn.call(el, window.event); 
      };   
      el.attachEvent('on' + type, f) 
     } 
    } 
    // Collect File IDs and Initiate Upload for Submit 
    MyUp.prototype.starter = function() 
    { 
     if(window.File && window.FileReader && window.FileList && window.Blob) 
     { 

      var browsed_file_id = $("#"+this.settings.form_id).find("input[type='file']").eq(0).attr("id"); 
      document.getElementById(browsed_file_id).addEventListener('change', this.read, false); 
      document.getElementById('submit_upload_'+this.fuid).addEventListener('click', this.submit, true); 
     } 
     else alert(browser_cant_read_message); 
    } 
    // Begin Upload on Click 
    MyUp.prototype.submit = function() 
    { 
     self.begin(); 
    } 
    // Initiate Upload Iterator 
    MyUp.prototype.begin = function() 
    { 
     if(this.browsed_files.length > 0) 
     { 
      for(var k=0; k<this.browsed_files.length; k++) 
      { 
       var file = this.browsed_files[k]; 
       this.myupAjax(file,k); 
      } 
      this.browsed_files = []; 
     } 
     else alert(no_files_chosen_message); 
    } 
    // Ajax Upload 
    MyUp.prototype.myupAjax = function(file,i) 
    { 
     if(file[i]!== undefined) 
     { 
      var id = file_id = self.uid(file[i].name), 
       rawname = file[i].name.substr(0, file[i].name.lastIndexOf('.')) || file[i].name, 
       extension = self.ext(file[i].name, false), 
       browsed_file_id = $("#"+this.settings.form_id).find("input[type='file']").eq(0).attr("id"); 
       path = this.settings.fixed ? this.settings.fixed : String($('input#upload-actionpath-'+this.fuid).val()), 
       pathcheck = String(this.settings.pathcheck), 
       removed_file = $("#"+id).val(), 
       newname = String($('input#rename_upfile_id_'+id).val()), 
       new_name = newname === '' || newname === 'undefined' || newname === undefined ? file[i].name : newname+'.'+extension, 
       removed = this.settings.removed, 
       loading = this.settings.loading, 
       fixedchars = this.settings.fixed; 
      // if the file is removes, skip to the next file 
      if(removed_file !== '' && removed_file !== undefined && removed_file == id) self.myupAjax(file,i+1); 
      else 
      { 
       var myupData = new FormData(); 
       myupData.append('upload_file',file[i]); 
       myupData.append('upload_file_id',id); 
       myupData.append('max_file_size',this.settings.maxsize); 
       myupData.append('upload_path',path);  
       myupData.append('new_name',new_name); 
       myupData.append('extension',extension); 
       myupData.append('uploader',this.settings.uploader); 
       myupData.append('act','upload'); 
       myupData.append('nonce',myup.nonce);     
       $.ajax(
       { 
        type  : 'POST', 
        url   : myup.ajaxurl+'?action=myup-uploads', 
        data  : myupData, 
        id   : id, 
        fuid  : this.fuid, 
        new_name : new_name, 
        rawname  : rawname, 
        extension : extension, 
        path  : path, 
        pathcheck : pathcheck, 
        removed  : removed, 
        loading  : loading, 
        fixedchars : fixedchars, 
        cache  : false, 
        contentType : false, 
        processData : false, 
        beforeSend : function(xhr, settings) 
        { 
         // I added this alert because it shows me that when I'm using the first instance 
         // after the first file uploads, it starts looking to the file input field 
         // from the second instance 
         alert('path: '+settings.path+' pathcheck: '+settings.pathcheck); 

         // in here I do a bunch of security stuff before uploading 
        }, 
        xhr: function() 
        { 
         // my progress bar function here 
        }, 
        success : function(response) 
        { 
         setTimeout(function() 
         { 
          if(response.indexOf(id) != -1) 
          { 
           // do success stuff, like green checkmark, remove table row, etc. 
          } 
          else 
          { 
           // do failure stuff, like red x and error message 
          } 

          // AND HERE IS THE IMPORTANT PART 
          // THIS SAYS TO GO ON TO THE NEXT FILE IN THE ARRAY 
          // BUT WHEN USING THE FIRST INSTANCE 
          // IT SENDS US TO THE SECOND INSTANCE'S FILE READER 
          // if I'm uploading with the second form it's fine 
          if(i+1 < file.length) self.myupAjax(file,i+1); 
         },500); 
        } 
       }); 
      } 
     } 
    } 
    this.starter(); 
} 
window.MyUp = MyUp; 
window.myupRemove = myupRemove; 
}); 

注意向上面的代碼末尾的註釋塊代碼片段,其中評論全部大寫。這就是完成文件ajax上傳的地方,然後將我們發回去做下一個。

所以,基本上,重申一下,如果我在頁面上有兩種形式,當我使用第二種形式時,一切正常。當我使用第一個表單時,第一個文件將很好地上傳,但是它開始在第二個實例的輸入字段中查找下一個文件。

有沒有比較簡單的解決方案呢?

+0

您能否提供一個示例頁面? – 19greg96 2014-09-13 13:13:37

+0

你說它運行正常,當你使用第二個實例上傳時。如果你有,比方說,3個或更多的實例?所有後來的(從第二個)工作? – melancia 2014-09-13 13:19:00

+1

在我看來,當你在這裏聲明'self'時:'var self = this;'它保留對最後一個實例的引用,所以無論它是第二個,第三等,實例。 – melancia 2014-09-13 13:24:08

回答

0

那麼我無法像Alex Gyoshev所建議的那樣工作(我的這一點毫無疑問),所以我決定完全擺脫對象/原型路線。我不是在表單中創建實例,而是在全局空間中聲明一個MyUpConfig變量,作爲一個空數組。然後在每個html表單中,我添加MyUpConfig['.$uid.'] =,然後添加我的設置數組。

然後在js文件中,我綁定每個表單的文件輸入並將事件提交給唯一標識,並將每個輸入字段的文件數據存儲在如下數組中:TheFiles[uid] = files。因此,當上傳提交發生時,它會從上傳按鈕元素獲取uid,並且只會嘗試上傳存儲在與該唯一ID匹配的數組中的文件。

我現在可以在同一頁面上使用多個上傳表單。它們可以同時運行而不受干擾。

0

在發佈的代碼中,構造函數聲明瞭所有的原型方法。這會導致每次創建新的MyUp對象時都會覆蓋它們,並且它們將使用最後一個對象的配置。

function MyUp(config) { 
    /// ... 
    var self = this; 
    MyUp.prototype.foo = function() { 
     // use `self` 
    }; 
} 

相反,你應該申報的方法一次並使用this參考獲取任何附加的對象:

function MyUp(config) { 
    /// ... 
} 
MyUp.prototype.foo = function() { 
    // use `this` 
}; 

如果您需要靜態成員的類(方法或變量不附加到任何實例),如果要公開使用它們,則可以將它們聲明爲函數的字段 - MyUp.begin = function() {}。或在模塊內部,如果是私人:

(function() { 
    var uploadsInProgress = 0; 

    // declare MyUp and use uploadsInProgress; it will be hidden from customer code 

    window.MyUp = MyUp; 
})(); 

我不能完全肯定這是否會解決上傳問題,但它絕對會讓你保持清醒的多個實例化對象時。

+0

非常感謝。我現在要測試這個。 – 2014-09-13 16:07:26

+0

所以我已經將函數聲明爲MyUp.functionname = function(){}。 – 2014-09-13 16:21:01

+0

...對不起,繼續。我將所有原型移到構造函數的括號之外。不,我得到undefined不是一個函數。我還需要做些什麼? – 2014-09-13 16:21:46

相關問題