Skip to content

Instantly share code, notes, and snippets.

@falkolab
Created July 30, 2014 08:16
Show Gist options
  • Save falkolab/f160f446d0bda8a69172 to your computer and use it in GitHub Desktop.
Save falkolab/f160f446d0bda8a69172 to your computer and use it in GitHub Desktop.
Download file by http with progress (NodeJS)
function download(fileUrl, apiPath, callback) {
var url = require('url'),
http = require('http'),
p = url.parse(fileUrl),
timeout = 10000;
var file = fs.createWriteStream(apiPath);
var timeout_wrapper = function( req ) {
return function() {
console.log('abort');
req.abort();
callback("File transfer timeout!");
};
};
console.log('before');
var request = http.get(fileUrl).on('response', function(res) {
console.log('in cb');
var len = parseInt(response.headers['content-length'], 10);
var downloaded = 0;
res.on('data', function(chunk) {
file.write(chunk);
downloaded += chunk.length;
process.stdout.write("Downloading " + (100.0 * downloaded / len).toFixed(2) + "% " + downloaded + " bytes" + isWin ? "\033[0G": "\r");
// reset timeout
clearTimeout( timeoutId );
timeoutId = setTimeout( fn, timeout );
}).on('end', function () {
// clear timeout
clearTimeout( timeoutId );
file.end();
console.log(file_name + ' downloaded to: ' + apiPath);
callback(null);
}).on('error', function (err) {
// clear timeout
clearTimeout( timeoutId );
callback(err.message);
});
});
// generate timeout handler
var fn = timeout_wrapper( request );
// set initial timeout
var timeoutId = setTimeout( fn, timeout );
}
@ivanm376
Copy link

ivanm376 commented Sep 7, 2017

var len = parseInt(response.headers['content-length'], 10);

should be

var len = parseInt(res.headers['content-length'], 10);

@eldoy
Copy link

eldoy commented Jul 23, 2019

Updated version that actually works and with promises and ES6:

const { parse } = require('url')
const http = require('https')
const fs = require('fs')
const { basename } = require('path')

const TIMEOUT = 10000

module.exports = function(url, path) {
  const uri = parse(url)
  if (!path) {
    path = basename(uri.path)
  }
  const file = fs.createWriteStream(path)

  return new Promise(function(resolve, reject) {
    const request = http.get(uri.href).on('response', function(res) {
      const len = parseInt(res.headers['content-length'], 10)
      let downloaded = 0
      let percent = 0
      res
        .on('data', function(chunk) {
          file.write(chunk)
          downloaded += chunk.length
          percent = (100.0 * downloaded / len).toFixed(2)
          process.stdout.write(`Downloading ${percent}% ${downloaded} bytes\r`)
        })
        .on('end', function() {
          file.end()
          console.log(`${uri.path} downloaded to: ${path}`)
          resolve()
        })
        .on('error', function (err) {
          reject(err)
        })
    })
    request.setTimeout(TIMEOUT, function() {
      request.abort()
      reject(new Error(`request timeout after ${TIMEOUT / 1000.0}s`))
    })
  })
}

Include it in another file and use it:

const download = require('./download.js')
const url = 'https://raw.githubusercontent.com/replace-this-with-your-remote-file'
console.log('Downloading ' + url)

async function run() {
  console.log('Downloading file')
  try {
    await download(url, 'server')
    console.log('Download done')
  } catch (e) {
    console.log('Download failed')
    console.log(e.message)
  }
}

run()

@abdolrhman
Copy link

@fugroup can i stream the file that is being download via for ex: socket io?

@eldoy
Copy link

eldoy commented Sep 4, 2019

@abdolrhman Not without a rewrite, it's intended for command line scripts (node).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment