Javascript AngularJS:跟踪同时上传的每个文件的状态

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13591345/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 12:25:41  来源:igfitidea点击:

AngularJS: tracking status of each file being uploaded simultaneously

javascriptangularjs

提问by Scott

Upon pressing a submit button, an array of files ($scope.files, can be as little as one file, as many as the user wants) gets submitted via FormDataand XMLHttpRequestin my angular function $scope.uploadFiles:

按下提交按钮后,将$scope.files通过FormDataXMLHttpRequest在我的 angular 函数中提交一组文件(, 可以是一个文件,可以是用户想要的数量)$scope.uploadFiles

$scope.uploadFiles = function() {
    for (var i in $scope.files) {
        var form = new FormData();
        var xhr = new XMLHttpRequest;

            // Additional POST variables required by the API script
        form.append('destination', 'workspace://SpacesStore/' + $scope.currentFolderUUID);
        form.append('contenttype', 'idocs:document');
        form.append('filename', $scope.files[i].name);
        form.append('filedata', $scope.files[i]);
        form.append('overwrite', false);

        xhr.upload.onprogress = function(e) {
            // Event listener for when the file is uploading
            $scope.$apply(function() {
                var percentCompleted;
                if (e.lengthComputable) {
                    percentCompleted = Math.round(e.loaded / e.total * 100);
                    if (percentCompleted < 1) {
                                            // .uploadStatus will get rendered for the user via the template
                        $scope.files[i].uploadStatus = 'Uploading...';
                    } else if (percentCompleted == 100) {
                        $scope.files[i].uploadStatus = 'Saving...';
                    } else {
                        $scope.files[i].uploadStatus = percentCompleted + '%';
                    }
                }
            });
        };

        xhr.upload.onload = function(e) {
            // Event listener for when the file completed uploading
            $scope.$apply(function() {
                $scope.files[i].uploadStatus = 'Uploaded!'
                setTimeout(function() {
                    $scope.$apply(function() {
                        $scope.files[i].uploadStatus = '';
                    });
                }, 4000);
            });
        };

        xhr.open('POST', '/path/to/upload/script');
        xhr.send(form);
    }

}

The problem is that var iincrements in the initial for loop for each file, and by the time the event listeners fire, ihas already incremented past the intended files[i]value needed, only effecting the last in the array. I use .uploadStatusas a means to interactively show the progress of each individual file to the user, therefor I would need to have separate event listeners for each array element in $scope.files. How would I assign and track events for individual array elements in Angular?

问题是var i每个文件的初始 for 循环中的增量,到事件侦听器触发时,i已经增量超过files[i]所需的预期值,仅影响数组中的最后一个。我.uploadStatus用作一种向用户交互式显示每个单独文件的进度的方法,因此我需要为$scope.files. 我将如何为 Angular 中的单个数组元素分配和跟踪事件?

UPDATE

更新



I reworked the two event listeners, with a little success, but I'm still experiencing odd behavior:

我重新设计了两个事件侦听器,取得了一些成功,但我仍然遇到奇怪的行为:

xhr.upload.onprogress = (function(file) {
    // Event listener for while the file is uploading
    return function(e) {
        $scope.$apply(function() {
            var percentCompleted = Math.round(e.loaded / e.total * 100);
            if (percentCompleted < 1) {
                file.uploadStatus = 'Uploading...';
            } else if (percentCompleted == 100) {
                file.uploadStatus = 'Saving...';
            } else {
                file.uploadStatus = percentCompleted + '%';
            }
        });
    }
})($scope.files[i]);

xhr.upload.onload = (function(file, index) {
    // Event listener for when the file completed uploading
    return function(e) {
        $scope.$apply(function() {
            file.uploadStatus = 'Uploaded!'
            setTimeout(function() {
                $scope.$apply(function() {
                    $scope.files.splice(index,1);
                });
            }, 2000);
        });
    }
})($scope.files[i], i);

.onprogressseems to go off without a hitch, but with some small changes made to .onload, I am now seeing a lot of weird behavior with AngularJS's two-way binding for its templates. for each elemnt in array $scope.filesa status is given, using the the aforementioned .uploadStatusproperty. Now I am having the setTimeoutsplice the elements from the array, via the ivariable that's getting passed in to the self-executing function. Oddly enough the uploads are capping at around 6 simultaneous uploads at a time now, that must be a server side issue, but I'm noticing that as array elements are getting spliced, the ng-repeatin the template is acting bizarrely where it will splice anelement, not necessarily the ones it should. I've also noticed that there are often entries that do not get spliced after the 2000 millisecond threshold is hit.

.onprogress似乎很顺利,但是对 进行了一些小的更改.onload,我现在看到 AngularJS 对其模板的双向绑定有很多奇怪的行为。对于数组中$scope.files的每个元素,使用上述.uploadStatus属性给出一个状态。现在我通过传递给自执行函数setTimeouti变量拼接数组中的元素。奇怪的是,上传现在一次限制在大约 6 个同时上传,这一定是服务器端的问题,但我注意到随着数组元素被拼接,ng-repeat模板中元素,不一定是它应该的元素。我还注意到,经常有条目在达到 2000 毫秒阈值后没有被拼接。

This is reminiscent of the original problem where the variable iis unreliable when referenced throughout the triggering of the event listeners? Now I am passing it in to the anonymous self-executing .onloadfunction, and the splice is using it to determine what array element it should remove, but it isn't necessarily removing the correct one, and often leaving other elements in the array when it should be removing them.

这让人想起最初的问题,即i在整个事件侦听器的触发过程中引用变量时,变量是不可靠的?现在我将它传递给匿名自执行.onload函数,拼接使用它来确定它应该删除哪个数组元素,但它不一定删除正确的元素,并且经常在它时将其他元素留在数组中应该删除它们。

采纳答案by abhaga

The indexyou pass to the event handlers refer to the indices in the original array. After every successful call to splice, the array changes and indices are no longer pointing to the same thing.

index原始数组中传递给事件处理程序参照指标。每次成功调用 后splice,数组都会发生变化,索引不再指向同一事物。

$scope.files = ['a.jpg', 'b.jpg', 'c.jpg'];
// $scope.files[1] = 'b.jpg'

$scope.files.splice(1,1);
// $scope.files = ['a.jpg', 'c.jpg']
// $scope.files[1] = 'c.jpg'

回答by appbootup

AngularJs 1.5.5 changes the approach by providing callback eventhandlers for upload progress.

AngularJs 1.5.5 通过为上传进度提供回调事件处理程序来改变方法。

Reference - How to track progress using eventHandlers and uploadEventHandlers in angularjs 1.5.5 with $http or $resource?

参考 -如何使用 $http 或 $resource 在 angularjs 1.5.5 中使用 eventHandlers 和 uploadEventHandlers 跟踪进度?

var uploadData = new FormData();
uploadData.append('file', obj.lfFile);
var fileData = angular.toJson({
    'FileName': obj.lfFile.name,
    'FileSize': obj.lfFile.size
});
uploadData.append('fileData', fileData)

$http({
    method: 'POST',
    url: vm.uploadPath,
    headers: {
        'Content-Type': undefined,
        'UserID': vm.folder.UserID,
        'ComputerID': vm.folder.ComputerID,
        'KeepCopyInCloud': vm.keepCopyInCloud,
        'OverWriteExistingFile': vm.overwriteExistingFile,
        'RootFileID': vm.folder.RootFileID,
        'FileName': obj.lfFile.name,
        'FileSize': obj.lfFile.size
    },
    eventHandlers: {
        progress: function(c) {
            console.log('Progress -> ' + c);
            console.log(c);
        }
    },
    uploadEventHandlers: {
        progress: function(e) {
            console.log('UploadProgress -> ' + e);
            console.log(e);
        }
    },
    data: uploadData,
    transformRequest: angular.identity
}).success(function(data) {
    console.log(data);
}).error(function(data, status) {
    console.log(data);
    console.log(status);
});