2015-12-28 27 views
5

Ho il seguente codice HTML:Javascript Promesse con FileReader()

<input type='file' multiple> 

Ed ecco il mio codice JS:

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

Quindi la mia domanda è, come posso fare questo ciclo sincrono? È la prima volta che il file termina il caricamento, quindi passa al file successivo. Qualcuno mi ha detto di usare JS Promises. Ma non posso farlo funzionare. Ecco quello che sto cercando:

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

Grazie in anticipo ...

+3

Promises sono usati per operazioni asincrone. –

+0

Come posso renderlo sincrono allora? Ho studiato su internet, tutti dicono che rende il codice sincrono –

+0

@ZahidSaeed: No, le promesse non rendono il codice sincrono. Puoi indicare uno di quei posti che "tutti" dicono di fare? –

risposta

4

La natura di FileReader è che non si può fare il suo funzionamento sincrono.

Sospetto che non sia realmente necessario o che sia sincronizzato, solo che si desidera ottenere correttamente gli URL risultanti. Se è così, non penserei che le promesse potrebbero davvero aiutare. Invece, basta tenere traccia di quante sospeso le operazioni che avete in modo da sapere quando hai finito:

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

Oppure, se volete, per qualche ragione per leggere i file in sequenza (ma ancora in modo asincrono), è possibile farlo programmando la prossima chiamata solo quando quello precedente è completo:

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

Se si vuole fare in modo sequenziale (non in sincronia) con promesse, si potrebbe fare qualcosa di simile:

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

tu sei l'uomo! La seconda parte (la nuova Promessa) è esattamente ciò di cui avevo bisogno per ottenere il mio FileReader da restituire come Promessa, se necessario. Grazie! – skplunkerin

+0

Ho dovuto eseguire 'new Promise ((resolve, reject) => {' per farlo funzionare ... (Necessario '').) – Vaccano

1

abbiamo modificato midos rispondono alle farlo funzionare quanto segue:

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

Ecco un'altra modifica alla risposta di Jens' (piggybacking off risposta di Mido) per verificare ulteriormente la dimensione del file:

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