【问题标题】:Jquery how clean formData object in my own upload pluginJquery如何在我自己的上传插件中清理formData对象
【发布时间】:2015-06-05 12:32:59
【问题描述】:

我尝试创建自己的插件来通过 ajax 上传文件。
如果上传后重新加载输入文件所在的页面,似乎效果很好。
如果有输入文件的页面在上传后没有重新加载(因为只重新加载了ajax内容)IE和Chrome有问题,因为要上传的文件附加到上一个刚刚上传的文件(用firefox可以)。
我试图通过在第一次上传后清理输入文件来修复它,但这样使用 IE 和 Chrome 我无法再上传其他文件。

我的修复

complete: function () {
       defaults.onFinish.call(this);
       // If page where is the input file not reloaded
       // after upload files IE and Chrome not working
       $this.replaceWith($this.val('').clone(true));
       $this.val('');
}

事实上,我会在每次上传后清理 formData 对象,但我无法做到

我的插件

;(function ($, window, document, undefined) {

    // Function-level strict mode syntax
    'use strict';

    $.fn.ajaxUpload = function(options) {

        var defaults = {
            num_files       : 0,
            max_files       : 2,
            max_concurrent  : 10,
            max_filesize    : 1024 * 4096,
            php_max_size    : 1024 * 8192,
            allowed_types   : ['jpeg','jpg'],
            ajax_url        : 'action.php',
            var_name        : 'file',
            extra_fields    : {},
            onFinish        : function() {}
        };

        var options = $.extend(defaults, options);

        return this.each(function() {

            var $this = $(this);

            $this.on('change', function() {

                var files = $this[0].files;
                var len = files.length;
                var items = 0;
                var diff_files = parseInt(defaults.max_files - defaults.num_files - len);

                if(diff_files < 0) {
                    return false;
                }
                if(!maxUploadFiles(len, defaults.max_concurrent)) {
                    return false;
                }
                var formdata = new FormData();

                jQuery.each(files, function(i, file) {
                    if(!isOverSized(file, defaults.max_filesize)) {
                        return false;
                    }
                    if(!isAllowedTypes(file, defaults.allowed_types)) {
                        return false;
                    }
                    if(!totalFilesSize(file, defaults.php_max_size)) {
                        return false;
                    }

                    formdata.append(defaults.var_name + '['+i+']', file);
                    items++;
                });

                // Append extra data to formdata
                $.each(defaults.extra_fields, function(name, value) {
                    formdata.append(name, value);
                });

                // Check that files have passed all test
                if (len != items) { return false; }

                $.ajax({
                    url: defaults.ajax_url,
                    data: formdata,
                    cache: false,
                    contentType: false,
                    processData: false,
                    type: 'POST',
                    beforeSend: function () {
                    },
                    success: function(data) {
                        totalSize = 0;
                    },
                    complete: function () {
                        defaults.onFinish.call(this);

                        // If page where is the input file not reloaded
                        // after upload files IE and Chrome not working
                        //$this.replaceWith($this.val('').clone(true));
                        //$this.val('');
                    }
                });

            });
       });
    };

    var totalSize = 0;

    function totalFilesSize(file, php_max_size) {
        totalSize += file.size;
        if(totalSize > php_max_size) {
            totalSize = 0;
            return false;
        }
        return true;
    }
    function maxUploadFiles(len, max_concurrent) {
        if(len > max_concurrent) {
            return false;
        }
        return true;
    }
    function isAllowedTypes(file, allowed_types) {
        var ext = file.name.split('.').pop().toLowerCase();
        if(jQuery.inArray(ext, allowed_types) < 0) {
            return false;
        }
        return true;
    }
    function isOverSized(file, max_filesize) {
        if(file.size > max_filesize) {
            return false;
        }
        return true;
    }

})(jQuery, window, document);

根据你的说法,我应该做些什么来解决我的问题? 谢谢

编辑

我在完成时添加了这一行,它似乎可以工作

$this.val('');
$this.wrap('<form>').parent('form').trigger('reset');
$this.unwrap();
$this.replaceWith($this.clone());

【问题讨论】:

    标签: javascript jquery ajax plugins jquery-plugins


    【解决方案1】:

    您的插件的问题是您使用$this 保留对原始输入的引用,然后尝试用克隆替换它。因为你是克隆最好每次都获取一个新的引用所以你应该取消绑定和绑定。

    (function ($, window, document, undefined) {
    
        // Function-level strict mode syntax
        'use strict';
    
        $.fn.ajaxUpload = function (options) {
    
            var defaults = {
                num_files: 0,
                max_files: 2,
                max_concurrent: 10,
                max_filesize: 1024 * 4096,
                php_max_size: 1024 * 8192,
                allowed_types: ['jpeg', 'jpg'],
                ajax_url: 'action.php',
                var_name: 'file',
                extra_fields: {},
                onFinish: function () {}
            };
    
            var options = $.extend(defaults, options);
    
            var bindInput = function (elem) {
                var element = $(elem),
                bindFunc = function (evt) {
    
                    var files = evt.currentTarget.files;
                    var len = files.length;
                    var items = 0;
                    var diff_files = parseInt(defaults.max_files - defaults.num_files - len);
    
                     if (diff_files < 0) {
                         return false;
                     }
                     if (!maxUploadFiles(len, defaults.max_concurrent)) {
                         return false;
                     }
                     var formdata = new FormData();
    
                     jQuery.each(files, function (i, file) {
                         if (!isOverSized(file, defaults.max_filesize)) {
                             return false;
                         }
                         if (!isAllowedTypes(file, defaults.allowed_types)) {
                             return false;
                         }
                         if (!totalFilesSize(file, defaults.php_max_size)) {
                             return false;
                         }
    
                         formdata.append(defaults.var_name + '[' + i + ']', file);
                         items++;
                     });
    
                     // Append extra data to formdata
                     $.each(defaults.extra_fields, function (name, value) {
                         formdata.append(name, value);
                     });
    
                     // Check that files have passed all test
                     if (len != items) {
                         return false;
                     }
    
                     $.ajax({
                         url: defaults.ajax_url,
                         data: formdata,
                         cache: false,
                         contentType: false,
                         processData: false,
                         type: 'POST',
                         beforeSend: function () {},
                         success: function (data) {
                             totalSize = 0;
                         },
                         complete: function () {
                             defaults.onFinish.call(this);
                             var previous = $(evt.currentTarget);
                             previous.off('change', bindFunc);
                             var newElem = previous.val('').clone(true)
                             previous.replaceWith(newElem);
                             bindInput(newElem);
    
                         }
                     });
    
                 };
                 element.on('change', bindFunc);
             };
    
             return this.each(function () {
                 bindInput(this)
             });
        };
    
        var totalSize = 0;
    
        function totalFilesSize(file, php_max_size) {
            totalSize += file.size;
            if (totalSize > php_max_size) {
                totalSize = 0;
                return false;
            }
            return true;
        }
    
        function maxUploadFiles(len, max_concurrent) {
            if (len > max_concurrent) {
                return false;
            }
            return true;
        }
    
        function isAllowedTypes(file, allowed_types) {
            var ext = file.name.split('.').pop().toLowerCase();
            if (jQuery.inArray(ext, allowed_types) < 0) {
                return false;
            }
            return true;
        }
    
        function isOverSized(file, max_filesize) {
            if (file.size > max_filesize) {
                return false;
            }
            return true;
        }
    
    })(jQuery, window, document);
    

    {编辑}

    引发您的问题的问题是每个文件上传插件开发人员的噩梦。在开发插件时,您应该知道输入标签可能包含插件使用者设置的其他样式和事件处理程序,您必须保留这些样式和事件处理程序,否则您将破坏现有功能。

    出于安全原因,无法使用 javascript 更改输入类型文件的值。 SO中有很多关于这个的答案。搜索clear+input+file自己看看,最了不起的就是这个Clearing <input type='file' /> using jQuery

    如您所见,基本上有两种选择:

    1. 克隆输入并在克隆前调用 val('') (调用 jQuery $(input).val('') 与调用 input.value = '' 不同)。

      这种方法的问题是,例如,在 IE 中,清除文件输入时会调用此事件两次,您必须小心释放内存和对被替换输入的引用,同时 保留当前样式和事件处理程序不是由您的插件设置的

    2. 第二个更好,但也有问题。将您的输入包装在表单标签中并调用表单的重置方法。

      input.wrap('<form>').parent('form').trigger('reset');
      input.unwrap();
      

      检查the docs关于form标签的sintax,你会看到下面的引用

      注意:严禁将一个表单嵌套在另一个表单中。这样做可能会以不可预知的方式运行,具体取决于用户使用的浏览器。

      这背后的主要原因是您的插件可以应用于已经在表单中的输入标记,从而使您的 html 无效,因此您必须包装表单调用 reset 方法并立即删除此表单。还请记住,如果您将表单放在一边,可能会对其应用视觉样式,从而破坏用户界面。

    第二种选择更容易修复您的代码。只需像这样更改完整的回调。在这种情况下不需要克隆。

    complete: function () {
        defaults.onFinish.call(this);
        $this.wrap('<form>').parent('form').trigger('reset');
        $this.unwrap();
    }
    

    这种变化应该发生得如此之快,以至于用户不会注意到它们。我测试了大约 1000 个元素,没有可见的视觉故障。

    【讨论】:

    • 感谢您的解决方案。但我注意到在上传文件后使用 chrome,文件名仍保留在输入文件中。请查看我的编辑并告诉我您的想法。使用我的解决方案虽然我必须委托输入点击事件。谢谢
    • 非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2013-01-22
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 2018-05-02
    相关资源
    最近更新 更多