具有多个Ajax文件上传和进度的FileReader

时间:2018-08-21 16:00:57

标签: javascript ajax

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

<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移到函数外部,则可以上传图像,但仅显示一个图像预览和一个进度条。

有人知道正确的方法吗?

2 个答案:

答案 0 :(得分:3)

问题在于,运行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.);
                }
            }
        });
    }
});

我需要强调的是,我还没有完全阅读过您的代码,但希望这会引导您朝着正确的方向前进。

答案 1 :(得分:1)

根据您的问题描述,我认为:

  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()