45 votes

AngularJS : suivi du statut de chaque fichier téléchargé simultanément.

En appuyant sur un bouton de soumission, un tableau de fichiers ( $scope.files (il peut s'agir d'un seul fichier ou d'autant de fichiers que l'utilisateur le souhaite) est soumis par l'intermédiaire du site Web de la Commission européenne. FormData y XMLHttpRequest dans ma fonction angulaire $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);
    }

}

Le problème est que var i s'incrémente dans la boucle for initiale pour chaque fichier, et au moment où les écouteurs d'événements se déclenchent, i a déjà été incrémenté au-delà de la date prévue files[i] nécessaire, n'affectant que la dernière valeur du tableau. J'utilise .uploadStatus comme un moyen de montrer de manière interactive la progression de chaque fichier individuel à l'utilisateur, je devrais donc avoir des écouteurs d'événements distincts pour chaque élément du tableau dans le fichier $scope.files . Comment attribuer et suivre les événements pour les éléments individuels d'un tableau dans Angular ?

UPDATE


J'ai retravaillé les deux écouteurs d'événements, avec un peu de succès, mais je continue à avoir un comportement étrange :

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

.onprogress semble se dérouler sans problème, mais avec quelques petites modifications apportées à .onload Je constate maintenant un comportement étrange avec la liaison bidirectionnelle d'AngularJS pour ses modèles. for each elemnt in array $scope.files un statut est donné, en utilisant la méthode susmentionnée .uploadStatus propriété. Maintenant, j'ai le setTimeout couper les éléments du tableau, via la fonction i qui est passée dans la fonction d'auto-exécution. Assez curieusement, les téléchargements plafonnent à environ 6 téléchargements simultanés à la fois, ce doit être un problème côté serveur, mais je remarque que lorsque les éléments du tableau sont épissés, la variable ng-repeat dans le modèle se comporte bizarrement, il va épisser un mais pas nécessairement ceux qu'il devrait. J'ai également remarqué qu'il y a souvent des entrées qui ne sont pas épissées après que le seuil de 2000 millisecondes soit atteint.

Cela rappelle le problème original où la variable i n'est pas fiable lorsqu'il est référencé lors du déclenchement des récepteurs d'événements ? Maintenant, je le passe à la commande anonyme auto-exécutoire .onload et le splice l'utilise pour déterminer quel élément du tableau il doit supprimer, mais il ne supprime pas nécessairement le bon élément, et laisse souvent d'autres éléments dans le tableau alors qu'il devrait les supprimer.

6voto

abhaga Points 2587

El index que vous passez aux gestionnaires d'événements font référence aux indices du tableau d'origine. Après chaque appel réussi à splice le tableau change et les indices ne pointent plus vers la même chose.

$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'

4voto

SSR Points 6481

AngularJs 1.5.5 change l'approche en fournissant des callbacks eventhandlers pour la progression du téléchargement.

Référence - Comment suivre la progression en utilisant eventHandlers et uploadEventHandlers dans angularjs 1.5.5 avec $http ou $resource ?

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);
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X