2012-10-16 13 views
23

I wie unten einen einfachen Download von http Funktion gemacht habe (Fehlerbehandlung für simplifcation weggelassen):fs.createWriteStream erstellt nicht sofort eine Datei?

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

Da ich jedoch asynchron download() zig-mal nennen, es es selten berichtet beschweren Fehler auf fs.renameSync Datei kann unter tempFile.path nicht gefunden werden.

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

Ich benutzte die gleiche Liste von URLs, um es zu testen, und es scheiterte etwa 30% der Zeit. Die gleiche Liste von URLs funktionierte, wenn sie nacheinander heruntergeladen wurden.

Testing etwas mehr, fand ich heraus, dass der folgende Code

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

immer true nicht gedruckt wird, aber manchmal sind die erste Antwort druckt false.

Ich vermute, dass zu viele asynchrone fs.createWriteStream Aufrufe die Dateierstellung nicht garantieren können. Ist das wahr? Gibt es Methoden, um die Erstellung von Dateien zu garantieren?

Antwort

47

Sie sollten write auf Ihrem Schreibstream nicht aufrufen, bis Sie das 'open' Ereignis aus dem Stream erhalten haben. Die Datei wird nicht existieren, bis Sie dieses Ereignis sehen.

Für Ihre Funktion:

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

Für Ihren 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

Irgendwie dieser Code-Snippet downloaden nicht einige des letzten Bytes. Statt dessen höre ich "finish" auf "tempFile" jetzt und "res.pipe (tempFile)" anstatt es manuell zu machen. –

+6

Die [fs.js Quelle] (https://github.com/joyent/node/blob/master/lib/fs.js#L1690) zeigt an, dass es nicht nötig ist, vorher auf das "open" Ereignis zu warten ruft 'write' auf den Schreibstrom auf, da dies intern behandelt wird. – MikeM

+0

'open' wird in der Tat intern behandelt, aber es ist async, die Datei ist nicht offen, bis das' open' Ereignis ausgelöst wird. –

12

Die akzeptierte Antwort nicht einige der letzten Bytes für mich heruntergeladen haben.
Hier ist eine Q Version, die korrekt funktioniert (aber ohne die temporäre Datei).

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

Hinweis Ich bin statt end auf Antwort auf finish auf Datei-Stream hören.

0

Hier ist, was ich verwende, um es getan:

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