Ajax上传器在同一页面上的多个实例

问题描述:

我开发了一个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

那么我无法像Alex Gyoshev所建议的那样工作(我的这一点毫无疑问),所以我决定完全摆脱对象/原型路线。我不是在表单中创建实例,而是在全局空间中声明一个MyUpConfig变量,作为一个空数组。然后在每个html表单中,我添加MyUpConfig['.$uid.'] =,然后添加我的设置数组。

然后在js文件中,我绑定每个表单的文件输入并将事件提交给唯一标识,并将每个输入字段的文件数据存储在如下数组中:TheFiles[uid] = files。因此,当上传提交发生时,它会从上传按钮元素获取uid,并且只会尝试上传存储在与该唯一ID匹配的数组中的文件。

我现在可以在同一页面上使用多个上传表单。它们可以同时运行而不受干扰。

在发布的代码中,构造函数声明了所有的原型方法。这会导致每次创建新的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