34

I want to download file from https server using nodejs. I tried this function, but it works only with http:

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

4 Answers 4

27

You should use https module then. Quoting the docs:

HTTPS is the HTTP protocol over TLS/SSL. In Node this is implemented as a separate module.

The good news is that the request-related methods of that module (https.request(), https.get() etc.) support all the options that the ones from http do.

Sign up to request clarification or add additional context in comments.

1 Comment

Be sure you use gzip: true option if the response is gzipped. I spent almost a day figuring this out. My downloaded files ended up smaller than original and would not open correctly.
21

Using standard Node modules only:

const fs = require('fs');
const http = require('http');
const https = require('https');

/**
 * Downloads file from remote HTTP[S] host and puts its contents to the
 * specified location.
 */
async function download(url, filePath) {
  const proto = !url.charAt(4).localeCompare('s') ? https : http;

  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(filePath);
    let fileInfo = null;

    const request = proto.get(url, response => {
      if (response.statusCode !== 200) {
        fs.unlink(filePath, () => {
          reject(new Error(`Failed to get '${url}' (${response.statusCode})`));
        });
        return;
      }

      fileInfo = {
        mime: response.headers['content-type'],
        size: parseInt(response.headers['content-length'], 10),
      };

      response.pipe(file);
    });

    // The destination stream is ended by the time it's called
    file.on('finish', () => resolve(fileInfo));

    request.on('error', err => {
      fs.unlink(filePath, () => reject(err));
    });

    file.on('error', err => {
      fs.unlink(filePath, () => reject(err));
    });

    request.end();
  });
}

2 Comments

Do you really need to check the statusCode if you already have an on errorhandler? And why do you unlink the file only in the error handler and not in case of statusCode !== 200?
@Toxiro, yes, you do need to check statusCode since it's an HTTP error, not a data transfer error. But you're right, the file should be unlinked in case of statuseCode !== 200 too. Updated the code.
2

Keeping it simple:

var fs = require('fs-extra');
var fetch = require('node-fetch');

/**
 * Download a file to disk
 * @example downloadFile('https://orcascan.com', './barcode-tracking.html')
 * @param {string} fileUrl - url of file to download
 * @param {string} destPath - destination path
 * @returns {Promise} resolves once complete, otherwise rejects
 */
function downloadFile(fileUrl, destPath) {

    if (!fileUrl) return Promise.reject(new Error('Invalid fileUrl'));
    if (!destPath) return Promise.reject(new Error('Invalid destPath'));

    return new Promise(function(resolve, reject) {

        fetch(fileUrl).then(function(res) {
            var fileStream = fs.createWriteStream(destPath);
            res.body.on('error', reject);
            fileStream.on('finish', resolve);
            res.body.pipe(fileStream);
        });
    });
}

3 Comments

node-fetch is now esm only, so I had to swap the requires for import and change my code to a module but this still worked flawlessly!
fetch is now built into Node.js, eliminating the need for node-fetch
Sweet. Which version? Can you provide a revised example?
0
    const request = require('request');
/* Create an empty file where we can save data */
let file = fs.createWriteStream(`file.jpg`);
/* Using Promises so that we can use the ASYNC AWAIT syntax */        
await new Promise((resolve, reject) => {
    let stream = request({
        /* Here you should specify the exact link to the file you are trying to download */
        uri: 'https://LINK_TO_THE_FILE',
        headers: {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'en-US,en;q=0.9,fr;q=0.8,ro;q=0.7,ru;q=0.6,la;q=0.5,pt;q=0.4,de;q=0.3',
            'Cache-Control': 'max-age=0',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
        },
        /* GZIP true for most of the websites now, disable it if you don't need it */
        gzip: true
    })
    .pipe(file)
    .on('finish', () => {
        console.log(`The file is finished downloading.`);
        resolve();
    })
    .on('error', (error) => {
        reject(error);
    })
})
.catch(error => {
    console.log(`Something happened: ${error}`);
});

2 Comments

Please edit your answer and add some context by explaining how your answer solves the problem, instead of posting code-only answer. From Review

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.