{"id":16138,"library":"node-downloader-helper","title":"Node Downloader Helper","description":"node-downloader-helper is a robust, zero-dependency file downloader for Node.js, supporting HTTP and HTTPS protocols. It's built for environments like vanilla Node.js, Electron, and NW.js, providing features essential for reliable file transfers. Key functionalities include pause and resume capabilities, automatic retry mechanisms for failed downloads, handling of HTTP redirects, and the option to pipe downloaded content for on-the-fly processing. Currently stable at version 2.1.11, the library demonstrates an active release cadence, with frequent patch updates addressing bugs, performance optimizations, and feature enhancements. Its primary differentiators are its complete independence from third-party packages, minimizing its attack surface and simplifying its dependency tree, along with comprehensive error management and detailed progress statistics.","status":"active","version":"2.1.11","language":"javascript","source_language":"en","source_url":"https://github.com/hgouveia/node-downloader-helper","tags":["javascript","nwjs","node","nodejs","node.js","electron","download","resumable","resume","typescript"],"install":[{"cmd":"npm install node-downloader-helper","lang":"bash","label":"npm"},{"cmd":"yarn add node-downloader-helper","lang":"bash","label":"yarn"},{"cmd":"pnpm add node-downloader-helper","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The primary class for managing downloads. While CommonJS `require` is supported, ESM `import` is recommended in modern Node.js projects, especially with TypeScript.","wrong":"const DownloaderHelper = require('node-downloader-helper').DownloaderHelper;","symbol":"DownloaderHelper","correct":"import { DownloaderHelper } from 'node-downloader-helper';"},{"note":"CommonJS syntax shown in older examples. Ensure this is used if your project is not ESM-enabled or configured for TypeScript.","symbol":"DownloaderHelper (CommonJS)","correct":"const { DownloaderHelper } = require('node-downloader-helper');"},{"note":"When importing types in TypeScript, it's best practice to use `import type` to explicitly indicate a type-only import, which gets stripped during compilation.","wrong":"import { DownloadOptions } from 'node-downloader-helper';","symbol":"DownloadOptions (Type)","correct":"import type { DownloadOptions } from 'node-downloader-helper';"}],"quickstart":{"code":"import { DownloaderHelper } from 'node-downloader-helper';\nimport path from 'path';\nimport fs from 'fs';\n\nconst downloadUrl = 'https://proof.ovh.net/files/1Gb.dat'; // Example URL for a large file\nconst destinationFolder = path.join(__dirname, 'downloads');\n\n// Ensure the destination folder exists\nif (!fs.existsSync(destinationFolder)) {\n  fs.mkdirSync(destinationFolder, { recursive: true });\n}\n\nconst dl = new DownloaderHelper(downloadUrl, destinationFolder, {\n  fileName: 'test-file.dat',\n  retry: { maxRetries: 3, delay: 1000 }, // Retry 3 times with 1-second delay\n  resumeOnIncomplete: true,\n  maxRedirects: 5\n});\n\ndl.on('start', () => console.log(`Starting download for ${downloadUrl} to ${destinationFolder}`));\ndl.on('progress', (stats) => {\n  const progress = stats.progress.toFixed(2);\n  const speed = (stats.speed / 1024 / 1024).toFixed(2); // MB/s\n  console.log(`Progress: ${progress}% - Speed: ${speed} MB/s - Downloaded: ${(stats.downloaded / 1024 / 1024).toFixed(2)} MB`);\n});\ndl.on('end', () => console.log('Download Completed successfully!'));\ndl.on('error', (err) => console.error('Download Failed:', err.message));\ndl.on('timeout', () => console.error('Download Timed out!'));\ndl.on('skip', (info) => console.log(`Download skipped: ${info.message}`));\n\ndl.start().catch(err => {\n  console.error('Failed to initiate download:', err.message);\n});","lang":"typescript","description":"This quickstart demonstrates how to initialize `DownloaderHelper`, configure basic options like retries and a custom filename, and set up event listeners for monitoring download progress, completion, and errors. It also ensures the download directory exists before starting."},"warnings":[{"fix":"Always define an `.on('error')` listener on the `DownloaderHelper` instance and wrap the `.start()` call in a `.catch()` block.","message":"It is highly recommended to use both `.on('error')` and `.start().catch` for comprehensive error handling. If `on('error')` is not defined, an `unhandled error event` will be thrown by EventEmitter, potentially crashing your application.","severity":"gotcha","affected_versions":">=2.0.0"},{"fix":"Upgrade to `node-downloader-helper@2.1.8` or newer to resolve this memory leak. If upgrading is not possible, ensure robust error handling and manual stream management.","message":"Versions prior to `2.1.8` had a critical bug where the file stream was not properly closed after a download retry, leading to memory leaks and potential file corruption.","severity":"breaking","affected_versions":"<2.1.8"},{"fix":"If piping modifies the file, set `resumeOnIncomplete: false`. Consider external mechanisms for managing partial files or re-downloading the entire file on interruption.","message":"Using `resumeOnIncomplete: true` in conjunction with piping the download stream (e.g., for compression or encryption) can lead to corrupted files if the pipe modifies the content, as the resume mechanism assumes the raw file content.","severity":"gotcha","affected_versions":">=2.0.0"},{"fix":"Upgrade to `node-downloader-helper@2.1.5` or newer. This version includes fixes for resume behavior when encountering HTTP 200 instead of 206 and improved handling of HTTP to HTTPS redirects during resume.","message":"Downloads could fail to resume correctly if the server responds with HTTP status 200 instead of 206 for a partial content request, or if there's a redirect from HTTP to HTTPS that isn't handled properly by older versions.","severity":"gotcha","affected_versions":"<2.1.2 || <2.1.5"},{"fix":"If `keep alive` is desired for performance in Node.js 19+, explicitly enable it via `httpRequestOptions: { agent: new http.Agent({ keepAlive: true }) }` within the DownloaderHelper options.","message":"For Node.js 19 and newer, `keep alive` is disabled by default in `node-downloader-helper` (since v2.1.5). This might impact performance for multiple sequential downloads from the same host by preventing connection reuse.","severity":"gotcha","affected_versions":">=2.1.5 (Node.js >=19)"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Add an event listener for the 'error' event: `dl.on('error', (err) => console.error('Download failed:', err));`","cause":"The 'error' event was emitted by DownloaderHelper, but no listener was registered for it, causing the EventEmitter to throw an unhandled error.","error":"Error: Unhandled error event."},{"fix":"Increase the `maxRedirects` option in the DownloaderHelper constructor (e.g., `{ maxRedirects: 20 }`) or verify the URL for redirect loops.","cause":"The download URL experienced more HTTP redirects than the `maxRedirects` option allowed, or an infinite redirect loop occurred.","error":"Error: Too many redirects"},{"fix":"Upgrade to `node-downloader-helper@2.1.8` or newer to get the fix for stream management during retries.","cause":"This error can occur in older versions (pre-2.1.8) due to a bug where the internal file stream was not properly closed or recreated during retries, leading to attempts to write to a closed stream.","error":"Error: Cannot write to file stream after it has been closed or destroyed."},{"fix":"Upgrade to `node-downloader-helper@2.1.5` or newer, which includes fixes for these specific resume scenarios.","cause":"This can happen in older versions if the server responds with HTTP 200 (OK) instead of 206 (Partial Content) to a resume request, or if a redirect during resume (e.g., http to https) was not correctly handled.","error":"Download stuck or reports 0 bytes downloaded after a resume attempt."}],"ecosystem":"npm"}