【问题标题】:FileReader with multi Ajax file upload and progress具有多 Ajax 文件上传和进度的 FileReader
【发布时间】:2019-01-27 21:00:21
【问题描述】:

我有一个多文件输入字段:

<input type="file" class="image_file" multiple>

我正在使用 FileReader 在上传图像时显示它们的预览。

我现在还想在上传的每张图片上显示一个进度条。这是我尝试过的:

$('.image_file').change(function() {
    var input = $(this);
    var files = this.files;
    var total = files.length;
    var url = input.attr('data-url');

    for (var i = 0; i < total; i++) {
        var formData = new FormData();
        var file = files[i];

        formData.append('image_file', file);

        var reader = new FileReader();

        reader.onload = function(e) {
            var container = $('.photos .photo:not(.active):first');

            if (container.length) {
                container.css('background-image', 'url(' + e.target.result + ')').addClass('active uploading');
            }
        };

        reader.readAsDataURL(file);

        $.ajax({
            type: 'post',
            url: url,
            data: formData,
            cache: false,
            processData: false,
            contentType: false,
            xhr: function() {
                var myXhr = $.ajaxSettings.xhr();

                var progressElem = container.find('progress');

                if (myXhr.upload) {
                    myXhr.upload.addEventListener('progress', function(e) {
                        if (e.lengthComputable) {
                            progressElem.attr({
                                value: e.loaded,
                                max: e.total
                            });
                        }
                    }, false);
                }

                return myXhr;
            },
            success: function(result) {
                if (result.status == true) {
                    $('.success-message').show();
                }
                else {
                    alert('There was an error uploading your file.);
                }
            }
        });
    }
});

我遇到的问题在 xhr 函数的这一行:

var progressElem = container.find('progress');

图像预览出现,但 AJAX 上传不起作用。控制台中也没有显示错误。我认为因为var container 是在reader.onload 函数中设置的,所以xhr 函数无权访问它。

如果我将该 var 移到函数之外,图片上传会正常工作,但只会显示一个图片预览和一个进度条。

有人知道正确的方法吗?

【问题讨论】:

  • 先把var container = $('.photos .photo:not(.active):first');放在onload之外,你不在同一个作用域,ajax检索不到。试试它是否已经解决了你的问题。

标签: javascript ajax


【解决方案1】:

问题在于,当 for 循环运行时,会创建和删除一个 xhr。一旦代码完成,之前的 xhr 就会被销毁,因此它永远不会运行。

我解决这个问题的方法是不使用 jQuery 和/或为每个 for 循环创建一个新的 xmlhttprequest

var array = []; //ADDED HERE

$('.image_file').change(function() {
    var input = $(this);
    var files = this.files;
    var total = files.length;
    var url = input.attr('data-url');

    for (var i = 0; i < total; i++) {
        var formData = new FormData();
        var file = files[i];

        formData.append('image_file', file);

        var reader = new FileReader();

        reader.onload = function(e) {
            var container = $('.photos .photo:not(.active):first');

            if (container.length) {
                container.css('background-image', 'url(' + e.target.result + ')').addClass('active uploading');
            }
        };

        reader.readAsDataURL(file);

        array[array.Length] = $.ajax({ //ADDED HERE
            type: 'post',
            url: url,
            data: formData,
            cache: false,
            processData: false,
            contentType: false,
            xhr: function() {
                var myXhr = $.ajaxSettings.xhr();

                var progressElem = container.find('progress');

                if (myXhr.upload) {
                    myXhr.upload.addEventListener('progress', function(e) {
                        if (e.lengthComputable) {
                            progressElem.attr({
                                value: e.loaded,
                                max: e.total
                            });
                        }
                    }, false);
                }

                return myXhr;
            },
            success: function(result) {
                if (result.status == true) {
                    $('.success-message').show();
                } else {
                    alert('There was an error uploading your file.);
                }
            }
        });
    }
});

我需要强调的是,我没有完整地查看您的代码,但希望这会引导您朝着正确的方向前进。

【讨论】:

    【解决方案2】:

    查看您的问题描述,我假设:

    1. 图像预览有效,因为您提到“图像预览出现”
    2. 图片上传,因为您提到了“如果我将该 var 移到函数之外,图片上传就可以工作......”

    那么问题出在哪里?

    问题是您的变量 container 在您已经提到的 xhr() 函数中不可访问。

    解决办法是什么?

    对于你的问题有很多可能的解决方案,但我认为将 ajax 请求块移动到 reader.onload 内是更好的主意,因为变量 container 将可供子函数访问,并且只有在有效文件时才会调用它正在上传。

    $('.image_file').change(function() {
      var input = $(this);
      var files = this.files;
      var total = files.length;
      var url = input.attr('data-url');
    
      for (var i = 0; i < total; i++) {
        var formData = new FormData();
        var file = files[i];
        formData.append('image_file', file);
        var reader = new FileReader();
        reader.onload = function(e) {
          var container = $('.photos .photo:not(.active):first');
          if (container.length) {
            var ajaxFunction = function() {
              var myXhr = $.ajaxSettings.xhr();
    
              var progressElem = this.find('progress');
    
              if (myXhr.upload) {
                myXhr.upload.addEventListener('progress', function(e) {
                  if (e.lengthComputable) {
                    progressElem.attr({
                      value: e.loaded,
                      max: e.total
                    });
                  }
                }, false);
              }
    
              return myXhr;
            };
            container.css('background-image', 'url(' + e.target.result + ')').addClass('active uploading');
            $.ajax({
              type: 'post',
              url: url,
              data: formData,
              cache: false,
              processData: false,
              contentType: false,
              xhr: ajaxFunction.bind(container),
              success: function(result) {
                if (result.status == true) {
                  $('.success-message').show();
                } else {
                  alert('There was an error uploading your file.');
                }
              }
            });
          }
        };
    
        reader.readAsDataURL(file);
      }
    });
    .photo {
      display: none;
      height: 200px;
      width: 200px;
      float: left;
    }
    .active {
      display: block;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <input type="file" class="image_file" multiple data-url="http://httpbin.org/post">
    <div class="photos">
      <div class="photo">
        <progress></progress>
      </div>
      <div class="photo">
        <progress></progress>
      </div>
    </div>

    更新:使用bind()函数将变量container的当前值传递给ajaxFunction()

    【讨论】:

    • 您可以测试图片上传,但请记住您上传的图片会发送到httpbin.org/post。另外,为了测试只支持两个图像预览和两个进度条。
    猜你喜欢
    • 2011-06-18
    • 1970-01-01
    • 2015-03-12
    相关资源
    最近更新 更多