65

Update

The accepted answer was good for last year but today I would use the package everyone else uses: https://github.com/mikeal/request


Original

I'm trying to grab google's logo and save it to my server with node.js.

This is what I have right now and doesn't work:

        var options = {
            host: 'google.com',
            port: 80,
            path: '/images/logos/ps_logo2.png'
        };

        var request = http.get(options);

        request.on('response', function (res) {
            res.on('data', function (chunk) {
                fs.writeFile(dir+'image.png', chunk, function (err) {
                    if (err) throw err;
                    console.log('It\'s saved!');
                });
            });
        });

How can I get this working?

serv-inc
  • 35,772
  • 9
  • 166
  • 188
Mark
  • 32,293
  • 33
  • 107
  • 137

6 Answers6

93

A few things happening here:

  1. I assume you required fs/http, and set the dir variable :)
  2. google.com redirects to www.google.com, so you're saving the redirect response's body, not the image
  3. the response is streamed. that means the 'data' event fires many times, not once. you have to save and join all the chunks together to get the full response body
  4. since you're getting binary data, you have to set the encoding accordingly on response and writeFile (default is utf8)

This should work:

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

options = {
    host: 'www.google.com'
  , port: 80
  , path: '/images/logos/ps_logo2.png'
}

var request = http.get(options, function(res){
    var imagedata = ''
    res.setEncoding('binary')

    res.on('data', function(chunk){
        imagedata += chunk
    })

    res.on('end', function(){
        fs.writeFile('logo.png', imagedata, 'binary', function(err){
            if (err) throw err
            console.log('File saved.')
        })
    })

})
Ricardo Tomasi
  • 34,573
  • 2
  • 55
  • 66
  • 17
    how come you don't use semi-colons? – Zhianc Feb 05 '13 at 11:52
  • 3
    @jcdavid http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding – Ricardo Tomasi Feb 11 '13 at 01:19
  • 7
    this is terrible advice. never do `.setEncoding('binary')` and use `res.pipe(fs.createWriteStream())` instead of buffering the entire image in memory. you can use this library if you're too lazy: https://github.com/stream-utils/save-to – Jonathan Ong Dec 10 '13 at 10:00
  • 1
    @JonathanOng 2011, IIRC setting the encoding was required at the time. Agreed on streams, feel free to suggest an edit. – Ricardo Tomasi Jan 22 '14 at 03:37
  • This is only working for Google Logo image. How can I use above code to save this image https://eteacher.me/assets/images/eteacher.png – namal Aug 06 '18 at 16:21
  • Semicolons are not required in javascript. Also this is a standard: https://standardjs.com/ – Martin Muzatko Aug 28 '18 at 12:12
  • @MartinMuzatko "a" standard, not "the" standard. Just because some opinionated developers decided to *call* their semicolon aesthetic 'standard' doesn't mean it *is* a standard. Besides, TC39 warn against relying on ASI these days. – Jimmy Breck-McKye Nov 05 '18 at 15:09
  • @JimmyBreck-McKye this is true. The authors of ES also said that it is not really smart to rely on ASI. I am right now a bit torn between requiring a good knowledge about how ASI works and therefore avoid a few situations, or just use semicolons everywhere. To be honest, I like using fewer characters - it looks a bit cleaner. Nevertheless, people should know when semicolons are required either way, because I see developers adding semicolons everywhere.. After block definitions (functions, if, else, etc) – Martin Muzatko Nov 06 '18 at 14:35
  • And that's when you reach for `prettier` and stop thinking about "what styleguide to follow", knowing that it will put them in (or not) in a way that is syntactically correct as far as the spec is concerned. – Mike 'Pomax' Kamermans May 15 '19 at 01:49
40

This thread is old but I wanted to do same things with the https://github.com/mikeal/request package.

Here a working example

var fs      = require('fs');
var request = require('request');
// Or with cookies
// var request = require('request').defaults({jar: true});

request.get({url: 'https://someurl/somefile.torrent', encoding: 'binary'}, function (err, response, body) {
  fs.writeFile("/tmp/test.torrent", body, 'binary', function(err) {
    if(err)
      console.log(err);
    else
      console.log("The file was saved!");
  }); 
});
m4tm4t
  • 2,361
  • 1
  • 21
  • 37
  • 5
    The docs now say to set the encoding to null for binary or else it is considered utf8:https://github.com/request/request "encoding - Encoding to be used on setEncoding of response data. If null, the body is returned as a Buffer. Anything else (including the default value of undefined) will be passed as the encoding parameter to toString() (meaning this is effectively utf8 by default). (Note: if you expect binary data, you should set encoding: null.)" – Mike Cheel Feb 16 '17 at 00:13
  • I confirm, you should now set `{ encoding: null }` – m4tm4t Dec 12 '17 at 09:58
27

I suggest you use http-request, so that even redirects are managed.

var http = require('http-request');
var options = {url: 'http://localhost/foo.pdf'};
http.get(options, '/path/to/foo.pdf', function (error, result) {
    if (error) {
        console.error(error);
    } else {
        console.log('File downloaded at: ' + result.file);
    }
});
Drasill
  • 3,917
  • 29
  • 30
  • 2
    Have to admit, this is about as straight forward as an http download can get. – Tracker1 Sep 25 '12 at 17:09
  • Apparently http-get has reached its end of life, and the successor is http-request. – Deniz Ozger Aug 26 '14 at 20:59
  • Indeed, and it seems totally retro-compatible. – Drasill Nov 26 '14 at 13:04
  • 1
    that's a good solution, but consider that `http-request` in some cases like node web kit `nwjs` requires native modules (C static libraries), then you have to compile against the architecture if deploying it somewhere else than server environment. – loretoparisi Mar 16 '16 at 08:57
6

How about this?

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

options = {
    host: 'www.google.com' , 
    port: 80,
    path: '/images/logos/ps_logo2.png'
}

var request = http.get(options, function(res){

//var imagedata = ''
//res.setEncoding('binary')

var chunks = [];

res.on('data', function(chunk){

    //imagedata += chunk
    chunks.push(chunk)

})

res.on('end', function(){

    //fs.writeFile('logo.png', imagedata, 'binary', function(err){

    var buffer = Buffer.concat(chunks)
    fs.writeFile('logo.png', buffer, function(err){
        if (err) throw err
        console.log('File saved.')
    })

})
yuqin
  • 61
  • 1
  • 3
3

Cleanest way of saving image locally using request:

const request = require('request');
request('http://link/to/your/image/file.png').pipe(fs.createWriteStream('fileName.png'))

If you need to add authentication token in headers do this:

const request = require('request');
request({
        url: 'http://link/to/your/image/file.png',
        headers: {
            "X-Token-Auth": TOKEN,
        }
    }).pipe(fs.createWriteStream('filename.png'))                    
BlackBeard
  • 10,246
  • 7
  • 52
  • 62
0

I have an easier solution using fs.readFileSync(./my_local_image_path.jpg)

This is for reading images from Azure Cognative Services's Vision API

const subscriptionKey = 'your_azure_subscrition_key';
const uriBase = // **MUST change your location (mine is 'eastus')**
    'https://eastus.api.cognitive.microsoft.com/vision/v2.0/analyze';

// Request parameters.
const params = {
    'visualFeatures': 'Categories,Description,Adult,Faces',
    'maxCandidates': '2',
    'details': 'Celebrities,Landmarks',
    'language': 'en'
};

const options = {
    uri: uriBase,
    qs: params,
    body: fs.readFileSync(./my_local_image_path.jpg),
    headers: {
        'Content-Type': 'application/octet-stream',
        'Ocp-Apim-Subscription-Key' : subscriptionKey
    }
};

request.post(options, (error, response, body) => {
if (error) {
    console.log('Error: ', error);
    return;
}
let jsonString = JSON.stringify(JSON.parse(body), null, '  ');
body = JSON.parse(body);
if (body.code) // err
{
    console.log("AZURE: " + body.message)
}

console.log('Response\n' + jsonString);