wake-up-neo.com

Javascript verspricht mit FileReader ()

Ich habe den folgenden HTML-Code:

<input type='file' multiple>

Und hier ist mein JS-Code:

var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = function(){
    var fr = new FileReader();
    for(var i = 0; i < inputFiles.files.length; i++){
        fr.onload = function(){
            console.log(i) // Prints "0, 3, 2, 1" in case of 4 chosen files
        }
    }
    fr.readAsDataURL(inputFiles.files[i]);
}

Meine Frage ist also wie kann ich diese Schleife synchron machen? Das ist das Warten, bis die Datei vollständig geladen ist, und dann zur nächsten Datei übergehen. Jemand sagte mir, JS Promises zu verwenden. Aber ich kann es nicht schaffen zu arbeiten. Folgendes versuche ich:

var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = function(){
    for(var i = 0; i < inputFiles.files.length; i++){
        var fr = new FileReader();
        var test = new Promise(function(resolve, reject){
            console.log(i) // Prints 0, 1, 2, 3 just as expected
            resolve(fr.readAsDataURL(inputFiles.files[i]));
        });
        test.then(function(){
            fr.onload = function(){
                console.log(i); // Prints only 3
            }
        });
    };
}

Danke im Voraus...

11
Zahid Saeed

Wenn Sie dies mit Promises nacheinander (nicht synchron) durchführen möchten, können Sie Folgendes tun: 

var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = function(){
  var promise = Promise.resolve();
  inputFiles.files.map( file => promise.then(()=> pFileReader(file)));
  promise.then(() => console.log('all done...'));
}

function pFileReader(file){
  return new Promise((resolve, reject) => {
    var fr = new FileReader();  
    fr.onload = resolve;  // CHANGE to whatever function you want which would eventually call resolve
    fr.readAsDataURL(file);
  });
}
18
mido

Wir haben die Antwort von midos geändert, um Folgendes zu erreichen:

function readFile(file){
  return new Promise((resolve, reject) => {
    var fr = new FileReader();  
    fr.onload = () => {
      resolve(fr.result )
    };
    fr.readAsText(file.blob);
  });
}
6
Jens Lincke

Das Wesen von FileReader ist, dass Sie nicht synchron arbeiten können.

Ich vermute, Sie brauchen oder möchten nicht, dass es synchron ist, nur dass Sie die resultierenden URLs korrekt erhalten möchten. Wenn ja, würde ich nicht glauben, dass Versprechen wirklich helfen würden. Behalten Sie stattdessen die Anzahl der ausstehenden Vorgänge im Auge, damit Sie wissen, wann Sie fertig sind:

var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = function(){
    var data = [];      // The results
    var pending = 0;    // How many outstanding operations we have

    // Schedule reading all the files (this finishes before the first onload
    // callback is allowed to be executed)
    Array.prototype.forEach.call(inputFiles.files, function(file, index) {
        // Read this file, remember it in `data` using the same index
        // as the file entry
        var fr = new FileReader();
        fr.onload = function() {
            data[index] = fr.result;
            --pending;
            if (pending == 0) {
                // All requests are complete, you're done
            }
        }
        fr.readAsDataURL(file);
        ++pending;
    });
}

Oder wenn Sie aus irgendeinem Grund die Dateien sequentiell lesen möchten (aber immer noch asynchron), können Sie dies tun, indem Sie den nächsten Aufruf nur planen, wenn der vorherige abgeschlossen ist:

// Note: This assumes there is at least one file, if that
// assumption isn't valid, you'll need to add an up-front check
var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = function(){
    var index = 0;

    readNext();

    function readNext() {
        var file = inputFiles.files[index++];
        var fr = new FileReader();
        fr.onload = function() {
            // use fr.result here
            if (index < inputFiles.files.length) {
                // More to do, start loading the next one
                readNext();
            }
        }
        fr.readAsDataURL(file);
    }
}
5
T.J. Crowder

Ich aktualisiere Jens Lincke antwortete durch Hinzufügen eines funktionierenden Beispiels und führe die asynchrone/Warte-Syntax ein

function readFile(file) {
  return new Promise((resolve, reject) => {
    let fr = new FileReader();
    fr.onload = x=> resolve(fr.result);
    fr.readAsDataURL(file) // or readAsText(file) to get raw content
})}
function readFile(file) {
  return new Promise((resolve, reject) => {
    let fr = new FileReader();
    fr.onload = x=> resolve(fr.result);
    fr.readAsDataURL(file) // or readAsText(file) to get raw content
})}

async function load(e) {
  for(let [i,f] of [...e.target.files].entries() ){
    msg.innerHTML += `<h1>File ${i}: ${f.name}</h1>`;
    let p = document.createElement("pre");
    p.innerText += await readFile(f);
    msg.appendChild(p);
  }
}
<input type="file" onchange="load(event)" multiple />
<div id="msg"></div>
2

Hier ist eine weitere Änderung an Jens 'Antwort (Huckepack auf Midos Antwort), um zusätzlich die Dateigröße zu überprüfen:

function readFileBase64(file, max_size){
        max_size_bytes = max_size * 1048576;
        return new Promise((resolve, reject) => {
            if (file.size > max_size_bytes) {
                console.log("file is too big at " + (file.size / 1048576) + "MB");
                reject("file exceeds max size of " + max_size + "MB");
            }
            else {
            var fr = new FileReader();  
            fr.onloadend = () => {
                data = fr.result;
                resolve(data)
            };
            fr.readAsDataURL(file);
            }
        });
    }
0
JasonZiolo