Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fs.createWriteStream does not immediately create file?

Tags:

node.js

I have made a simple download from http function as below (error handling is omitted for simplifcation):

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

However, as I call download() tens of times asynchronously, it seldom reports error on fs.renameSync complaining it cannot find file at tempFile.path.

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

I used the same list of urls to test it, and it failed about 30% of time. The same list of urls worked when downloaded one by one.

Testing some more, I found out that the following code

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

does not always print true, but sometimes the first answer prints false.

I am suspecting that too many asynchronous fs.createWriteStream calls cannot guarantee the file creation. Is this true? Are there any methods to guarantee file creation?

like image 524
Jang-hwan Kim Avatar asked Oct 16 '12 02:10

Jang-hwan Kim


People also ask

Does FS writeFile create a new file?

Syntax – writeFile() functionA new file is created with the specified name. After writing to the file is completed (could be with or without error), callback function is called with error if there is an error reading file. If a file already exists with the name, the file gets overwritten with a new file.

How do you use createWriteStream?

createWriteStream() creates a writable stream in a very simple manner. After a call to fs. createWriteStream() with the filepath, you have a writeable stream to work with. It turns out that the response (as well as the request) objects are streams.

What is __ Dirname in node?

__dirname: It is a local variable that returns the directory name of the current module. It returns the folder path of the current JavaScript file.

What is FS createReadStream?

The function fs. createReadStream() allows you to open up a readable stream in a very simple manner. All you have to do is pass the path of the file to start streaming in. It turns out that the response (as well as the request) objects are streams.


2 Answers

You shouldn't call write on your tempFile write stream until you've received the 'open' event from the stream. The file won't exist until you see that event.

For your function:

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

For your 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')); }); 
like image 120
JohnnyHK Avatar answered Sep 22 '22 18:09

JohnnyHK


The accepted answer didn't download some of the last bytes for me.
Here's a Q version that works correctly (but without the temp file).

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

Note I'm listening to finish on file stream instead of end on response.

like image 37
Dan Abramov Avatar answered Sep 21 '22 18:09

Dan Abramov