Node Downloader Helper
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.
Common errors
-
Error: Unhandled error event.
cause The 'error' event was emitted by DownloaderHelper, but no listener was registered for it, causing the EventEmitter to throw an unhandled error.fixAdd an event listener for the 'error' event: `dl.on('error', (err) => console.error('Download failed:', err));` -
Error: Too many redirects
cause The download URL experienced more HTTP redirects than the `maxRedirects` option allowed, or an infinite redirect loop occurred.fixIncrease the `maxRedirects` option in the DownloaderHelper constructor (e.g., `{ maxRedirects: 20 }`) or verify the URL for redirect loops. -
Error: Cannot write to file stream after it has been closed or destroyed.
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.fixUpgrade to `node-downloader-helper@2.1.8` or newer to get the fix for stream management during retries. -
Download stuck or reports 0 bytes downloaded after a resume attempt.
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.fixUpgrade to `node-downloader-helper@2.1.5` or newer, which includes fixes for these specific resume scenarios.
Warnings
- gotcha 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.
- breaking 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.
- gotcha 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.
- gotcha 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.
- gotcha 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.
Install
-
npm install node-downloader-helper -
yarn add node-downloader-helper -
pnpm add node-downloader-helper
Imports
- DownloaderHelper
const DownloaderHelper = require('node-downloader-helper').DownloaderHelper;import { DownloaderHelper } from 'node-downloader-helper'; - DownloaderHelper (CommonJS)
const { DownloaderHelper } = require('node-downloader-helper'); - DownloadOptions (Type)
import { DownloadOptions } from 'node-downloader-helper';import type { DownloadOptions } from 'node-downloader-helper';
Quickstart
import { DownloaderHelper } from 'node-downloader-helper';
import path from 'path';
import fs from 'fs';
const downloadUrl = 'https://proof.ovh.net/files/1Gb.dat'; // Example URL for a large file
const destinationFolder = path.join(__dirname, 'downloads');
// Ensure the destination folder exists
if (!fs.existsSync(destinationFolder)) {
fs.mkdirSync(destinationFolder, { recursive: true });
}
const dl = new DownloaderHelper(downloadUrl, destinationFolder, {
fileName: 'test-file.dat',
retry: { maxRetries: 3, delay: 1000 }, // Retry 3 times with 1-second delay
resumeOnIncomplete: true,
maxRedirects: 5
});
dl.on('start', () => console.log(`Starting download for ${downloadUrl} to ${destinationFolder}`));
dl.on('progress', (stats) => {
const progress = stats.progress.toFixed(2);
const speed = (stats.speed / 1024 / 1024).toFixed(2); // MB/s
console.log(`Progress: ${progress}% - Speed: ${speed} MB/s - Downloaded: ${(stats.downloaded / 1024 / 1024).toFixed(2)} MB`);
});
dl.on('end', () => console.log('Download Completed successfully!'));
dl.on('error', (err) => console.error('Download Failed:', err.message));
dl.on('timeout', () => console.error('Download Timed out!'));
dl.on('skip', (info) => console.log(`Download skipped: ${info.message}`));
dl.start().catch(err => {
console.error('Failed to initiate download:', err.message);
});