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?
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.
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.
__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.
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.
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')); });
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With