Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


file.jpg
file.png
http.get
http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg
node.js:201         throw e; // process.nextTick error, or 'error' event on first tick               ^ Error: connect ECONNREFUSED     at errnoException (net.js:646:11)     at Object.afterConnect [as oncomplete] (net.js:637:18)
var http = require('http');
var fs = require('fs');

var file = fs.createWriteStream("file.jpg");
var request = http.get("http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg", function(response) {
  response.pipe(file);
});

Does this code close the file properly when the script ends or would it lose data?

If you want to support gathering information on the command line--like specifying a target file or directory, or URL--check out something like Commander.

Why would this error occur in the first place? The image is still online (im.glogster.com/media/2/5/24/10/5241033.png), so why does it produce an error? I'm still confused.

You can create an HTTP GET request and pipe its response into a writable file stream:

Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


var http = require('http'),
    fs = require('fs');

var request = http.get("yourfile.html", function(response) {
    if (response.statusCode === 200) {
        var file = fs.createWriteStream("copy.html");
        response.pipe(file);
    }
    // Add timeout.
    request.setTimeout(12000, function () {
        request.abort();
    });
});

Don't make file when you get an error, and prefere to use timeout to close your request after X secondes.

The following code is based on Brandon Tilley's answer :

Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


var fs = require('fs');
var http = require('http');

var download = function(url, dest, cb) {
    var file = fs.createWriteStream(dest);
    var request = http.get(url, function(response) {

        // check if response is success
        if (response.statusCode !== 200) {
            return cb('Response status was ' + response.statusCode);
        }

        response.pipe(file);

        file.on('finish', function() {
            file.close(cb);  // close() is async, call cb after close completes.
        });
    });

    // check for request error too
    request.on('error', function (err) {
        fs.unlink(dest);
        return cb(err.message);
    });

    file.on('error', function(err) { // Handle errors
        fs.unlink(dest); // Delete the file async. (But we don't check the result) 
        return cb(err.message);
    });
};
var fs = require('fs');
var request = require('request');

var download = function(url, dest, cb) {
    var file = fs.createWriteStream(dest);
    var sendReq = request.get(url);

    // verify response code
    sendReq.on('response', function(response) {
        if (response.statusCode !== 200) {
            return cb('Response status was ' + response.statusCode);
        }
    });

    // check for request errors
    sendReq.on('error', function (err) {
        fs.unlink(dest);
        return cb(err.message);
    });

    sendReq.pipe(file);

    file.on('finish', function() {
        file.close(cb);  // close() is async, call cb after close completes.
    });

    file.on('error', function(err) { // Handle errors
        fs.unlink(dest); // Delete the file async. (But we don't check the result)
        return cb(err.message);
    });
};

@Alex, nope, this is an error message and there's a return. So if response.statusCode !== 200 the cb on finish will never be called.

@ventura yep, btw, there's also the native https module that now can handle secure connections.

Despite the relative simplicity of this code, I would advise to use the request module as it handles much more protocols (hello HTTPS!) which aren't natively supported by http.

I had tried before, but didn't work properly.

It's more error prone without a doubt. Anyway, in any case where using request module is an option, I'd advise it as it's way higher level and thus, easier and efficient.

Speaking of handling errors, it's even better listening to request errors too. I'd even validate by checking response code. Here it's considered success only for 200 response code, but other codes might be good.

Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


var http = require('http');
var fs = require('fs');

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  var request = http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);  // close() is async, call cb after close completes.
    });
  });
}

A complete solution is:

What for are you storing request into a variable?

Without waiting for the finish event, naive scripts may end up with an incomplete file. Without scheduling the cb callback via close, you may get a race between accessing the file and the file actually being ready.

gfxmonk's answer has a very tight data race between the callback and the file.close() completing. file.close() actually takes a callback that is called when the close has completed. Otherwise, immediate uses of the of the file may fail (very rarely!).

he "stores" it into a variable so it does not become a global variable by default.

Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


download()
pipe
var http = require('http');
var fs = require('fs');

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  var request = http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);  // close() is async, call cb after close completes.
    });
  }).on('error', function(err) { // Handle errors
    fs.unlink(dest); // Delete the file async. (But we don't check the result)
    if (cb) cb(err.message);
  });
};

@Abdul Sounds like you are very new to node.js/javascript. Take a look at this tutorial: tutorialspoint.com/nodejs/nodejs_callbacks_concept.htm It's not complex.

@theGrayFox Because the code in this answer is way longer than the accepted one. :)

Don't forget to handle errors! The following code is based on Augusto Roman's answer.

Wonder why this doesn't have as many upvotes. Nice, man.

Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


const http = require("http");
const fs = require("fs");

function download(url, dest) {
    return new Promise((resolve, reject) => {
        const file = fs.createWriteStream(dest, { flags: "wx" });

        const request = http.get(url, response => {
            if (response.statusCode === 200) {
                response.pipe(file);
            } else {
                file.close();
                fs.unlink(dest, () => {}); // Delete temp file
                reject(`Server responded with ${response.statusCode}: ${response.statusMessage}`);
            }
        });

        request.on("error", err => {
            file.close();
            fs.unlink(dest, () => {}); // Delete temp file
            reject(err.message);
        });

        file.on("finish", () => {
            resolve();
        });

        file.on("error", err => {
            file.close();

            if (err.code === "EEXIST") {
                reject("File already exists");
            } else {
                fs.unlink(dest, () => {}); // Delete temp file
                reject(err.message);
            }
        });
    });
}
dest
file.close()
finish
fs.createWriteStream
fs.unlink()
statusCode !== 200
unlink()

Below is a modified solution (using ES6 and promises) which handles these problems.

Maybe node.js has changed, but it seems there are some problems with the other solutions (using node v8.1.2):

Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


function download(url, dest, callback) {
    var file = fs.createWriteStream(dest);
    var request = http.get(url, function (response) {
        response.pipe(file);
        file.on('finish', function () {
            file.close(callback); // close() is async, call callback after close completes.
        });
        file.on('error', function (err) {
            fs.unlink(dest); // Delete the file async. (But we don't check the result)
            if (callback)
                callback(err.message);
        });
    });
}
Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


var http = require('http');
var fs = require('fs');

function pDownload(url, dest){
  var file = fs.createWriteStream(dest);
  return new Promise((resolve, reject) => {
    var responseSent = false; // flag to make sure that response is sent only once.
    http.get(url, response => {
      response.pipe(file);
      file.on('finish', () =>{
        file.close(() => {
          if(responseSent)  return;
          responseSent = true;
          resolve();
        });
      });
    }).on('error', err => {
        if(responseSent)  return;
        responseSent = true;
        reject(err);
    });
  });
}

//example
pDownload(url, fileLocation)
  .then( ()=> console.log('downloaded file no issues...'))
  .catch( e => console.error('error while downloading', e));

for those who came in search of es6-style promise based way, I guess it would be something like:

Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


function resim(url) {

    var http = require("http");
    var fs = require("fs");
    var sayi = Math.floor(Math.random()*10000000000);
    var uzanti = ".jpg";
    var file = fs.createWriteStream("img/"+sayi+uzanti);
    var request = http.get(url, function(response) {
  response.pipe(file);
});

        return sayi+uzanti;
}

Path : img type : jpg random uniqid

Note
Rectangle 27 1

javascript How to download a file with Node.js (without using third party libraries)?


cb
file.close
var http = require('http');
var fs = require('fs');

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  var request = http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);
    });
  });
}

@Abdul You specify the callback with a function only if you need to do something when the file has been successfully fetched.

As Brandon Tilley said, but with the appropriate control flow:

Without waiting for the finish event, naive scripts may end up with an incomplete file.

the callback is confusing me. if I now invoke download(), how would I do it? What would I place as the cb argument? I have the download('someURI', '/some/destination', cb) but don't understand what to put in the cb

Note