同一页面上的多个Ajax Uploader实例



我开发了一个ajax文件上传器,如果页面上有一个实例,它会很好地工作。我正在尝试将其转换为允许多个实例,它几乎可以工作,只是当我在一个表单中上传多个文件时,它会很好地上传第一个文件,但对于所有后续文件,出于某种原因,它会从第二个表单实例指向下一个文件读取器。如果我使用上传表单的最后一个(第二个)实例进行上传,则多个文件上传即可。但如果我尝试用第一个实例上传,它会上传第一个文件,但所有后续文件都会被发送到从第二个实例输入的空文件中。我不明白为什么。我在整个上传表单中使用唯一的id名称,并在整个javascript函数中引用该唯一的id。我将尝试在下面包含所有必要的代码。

表单由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>';

这是一个精简版。script标记中的实例(包含设置)和html表单通过一个短代码输出。因此,页面上的多个短代码将输出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-9s]/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上传,然后将我们送回进行下一个上传。

所以,基本上,重申一下,如果我在页面上有两个表单,当我使用第二个表单时,一切都很好。当我使用第一个表单时,第一个文件会很好地上传,但随后它开始在第二个实例的输入字段中查找下一个文件。

有没有一个相对简单的解决方案?

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

我不完全确定这是否能解决上传问题,但它肯定会让你在实例化多个对象时保持理智。

我无法按照Alex Gyoshev建议的方式工作(毫无疑问,我的不足),所以我决定完全取消对象/原型路线。我没有在表单中创建实例,而是在全局空间中将MyUpConfig变量声明为空数组。然后在每个html表单中,我添加MyUpConfig['.$uid.'] =,然后添加我的设置数组。

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

我现在可以在同一页面上处理多个上传表单。它们可以在没有干扰的情况下同时运行。

最新更新