web-dev-qa-db-fra.com

En boucle dans FileReader, la sortie contient toujours la dernière valeur de la boucle

J'utilise l'API FileReader pour lire des fichiers en local.

<input type="file" id="filesx" name="filesx[]" onchange="readmultifiles(this.files)" multiple="" />

<script>
function readmultifiles(files) {
    var ret = "";
    var ul = document.querySelector("#bag>ul");
    while (ul.hasChildNodes()) {
        ul.removeChild(ul.firstChild);
    }
    for (var i = 0; i < files.length; i++)  //for multiple files
    {
        var f = files[i];
        var name = files[i].name;
        alert(name);
        var reader = new FileReader();  
        reader.onload = function(e) {  
            // get file content  
            var text = e.target.result;
            var li = document.createElement("li");
            li.innerHTML = name + "____" + text;
            ul.appendChild(li);
        }
        reader.readAsText(f,"UTF-8");
    }
}
</script>

Si l'entrée comprend 2 fichiers:

file1 ---- "content1"
file2 ---- "content2"

Je reçois cette sortie:

file2__content1
file2__content2

Comment réparer le code à afficher:

file1__content1
file2__content2
32
user384241

Le problème est que vous exécutez la boucle maintenant mais les rappels que vous définissez sont exécutés later (lorsque les événements sont déclenchés). Au moment où ils courent, la boucle est terminée et reste à quelle que soit la dernière valeur. Donc, il affichera toujours "fichier2" dans votre cas pour le nom.

La solution consiste à mettre le nom du fichier dans une fermeture avec le reste. Une façon de faire est de créer une expression de fonction immédiatement appelée (IIFE) et de passer le fichier en tant que paramètre à cette fonction:

for (var i = 0; i < files.length; i++) { //for multiple files          
    (function(file) {
        var name = file.name;
        var reader = new FileReader();  
        reader.onload = function(e) {  
            // get file content  
            var text = e.target.result; 
            var li = document.createElement("li");
            li.innerHTML = name + "____" + text;
            ul.appendChild(li);
        }
        reader.readAsText(file, "UTF-8");
    })(files[i]);
}

Vous pouvez également définir une fonction nommée et l'appeler normalement:

function setupReader(file) {
    var name = file.name;
    var reader = new FileReader();  
    reader.onload = function(e) {  
        // get file content  
        var text = e.target.result; 
        var li = document.createElement("li");
        li.innerHTML = name + "____" + text;
        ul.appendChild(li);
    }
    reader.readAsText(file, "UTF-8");
}

for (var i = 0; i < files.length; i++) {
    setupReader(files[i]);
}
81
Ben Lee

J'ai eu le même problème, résolu en utilisant Array.from

let files = e.target.files || e.dataTransfer.files;

Array.from(files).forEach(file => {
 // do whatever
})
2
lee shin

Au lieu d'utiliser var , utilisez let car la variable déclarée ne doit être utilisée que dans une boucle. 

for (let i = 0; i < files.length; i++)  //for multiple files
    {
        let f = files[i];
        let name = files[i].name;
        alert(name);
        let reader = new FileReader();  
        reader.onload = function(e) {  
            // get file content  
            let text = e.target.result;
            let li = document.createElement("li");
            li.innerHTML = name + "____" + text;
            ul.appendChild(li);
        }
        reader.readAsText(f,"UTF-8");
    }
1
Kyo Kurosagi