2012-10-16 11 views
23

ho fatto una semplice download dal http funzione di seguito (la gestione degli errori è omesso per simplifcation):fs.createWriteStream non crea immediatamente il file?

function download(url, tempFilepath, filepath, callback) { 
    var tempFile = fs.createWriteStream(tempFilepath); 
    http.request(url, function(res) { 
     res.on('data', function(chunk) { 
      tempFile.write(chunk); 
     }).on('end', function() { 
      tempFile.end(); 
      fs.renameSync(tempFile.path, filepath); 
      return callback(filepath); 
     }) 
    }); 
} 

Tuttavia, come lo chiamo io download() decine di volte in modo asincrono, esso segnala raramente errore fs.renameSync lamentarsi esso impossibile trovare il file a tempFile.path.

Error: ENOENT, no such file or directory 'xxx' 

Ho utilizzato lo stesso elenco di URL per testarlo e non è riuscito circa il 30% del tempo. Lo stesso elenco di URL ha funzionato quando scaricato uno per uno.

prova un po ', ho scoperto che il seguente codice

fs.createWriteStream('anypath'); 
console.log(fs.exist('anypath')); 
console.log(fs.exist('anypath')); 
console.log(fs.exist('anypath')); 

non sempre la stampa true, ma a volte le prime stampe risposta false.

Sospetto che troppe chiamate asincrone fs.createWriteStream non possano garantire la creazione del file. È vero? Esistono metodi per garantire la creazione di file?

risposta

47

Non è necessario chiamare write sul flusso di scrittura tempFile finché non si riceve l'evento 'open' dallo stream. Il file non esisterà fino a quando non vedrai quell'evento.

Per la vostra funzione:

function download(url, tempFilepath, filepath, callback) { 
    var tempFile = fs.createWriteStream(tempFilepath); 
    tempFile.on('open', function(fd) { 
     http.request(url, function(res) { 
      res.on('data', function(chunk) { 
       tempFile.write(chunk); 
      }).on('end', function() { 
       tempFile.end(); 
       fs.renameSync(tempFile.path, filepath); 
       return callback(filepath); 
      }); 
     }); 
    }); 
} 

per il test:

var ws = fs.createWriteStream('anypath'); 
ws.on('open', function(fd) { 
    console.log(fs.existsSync('anypath')); 
    console.log(fs.existsSync('anypath')); 
    console.log(fs.existsSync('anypath')); 
}); 
+0

In qualche modo, questo frammento di codice non scaricare alcuni degli ultimi byte. Invece, ascolto 'finish' su' tempFile' ora e 'res.pipe (tempFile)' invece di farlo manualmente. –

+6

La [fonte fs.js] (https://github.com/joyent/node/blob/master/lib/fs.js#L1690) indica che non è necessario attendere l'evento "open" prima chiamando 'write' nello stream di scrittura, poiché viene gestito internamente. – MikeM

+0

'open' è effettivamente gestito internamente, ma è asincrono, il file non è aperto fino a quando non viene emesso l'evento' open'. –

12

La risposta accettata non scaricare alcuni degli ultimi byte per me.
Ecco una versione Q che funziona correttamente (ma senza il file temporaneo).

'use strict'; 

var fs = require('fs'), 
    http = require('http'), 
    path = require('path'), 
    Q = require('q'); 

function download(url, filepath) { 
    var fileStream = fs.createWriteStream(filepath), 
     deferred = Q.defer(); 

    fileStream.on('open', function() { 
    http.get(url, function (res) { 
     res.on('error', function (err) { 
     deferred.reject(err); 
     }); 

     res.pipe(fileStream); 
    }); 
    }).on('error', function (err) { 
    deferred.reject(err); 
    }).on('finish', function() { 
    deferred.resolve(filepath); 
    }); 

    return deferred.promise; 
} 

module.exports = { 
    'download': download 
}; 

Nota sto ascoltando finish sul flusso di file, invece di end sulla risposta.

0

Ecco quello che uso per farlo fare:

function download(url, dest) { 
    return new Promise((resolve, reject) => { 
     http.get(url, (res) => { 
      if (res.statusCode !== 200) { 
       var err = new Error('File couldn\'t be retrieved'); 
       err.status = res.statusCode; 
       return reject(err); 
      } 
      var chunks = []; 
      res.setEncoding('binary'); 
      res.on('data', (chunk) => { 
       chunks += chunk; 
      }).on('end',() => { 
       var stream = fs.createWriteStream(dest); 
       stream.write(chunks, 'binary'); 
       stream.on('finish',() => { 
        resolve('File Saved !'); 
       }); 
       res.pipe(stream); 
      }) 
     }).on('error', (e) => { 
      console.log("Error: " + e); 
      reject(e.message); 
     }); 
    }) 
}; 
Problemi correlati