Downloads
rezo.download() saves a remote resource to a local file with real-time progress events. It returns a RezoDownloadResponse event emitter immediately, letting you track percentage, speed, ETA, and more while the download runs in the background.
Quick Start
import { Rezo } from 'rezo';
const rezo = new Rezo();
const download = rezo.download(
'https://releases.example.com/app-v2.0.zip',
'./downloads/app-v2.0.zip'
);
download.on('progress', (p) => {
console.log(`${p.percentage.toFixed(1)}% | ${(p.speed / 1024).toFixed(0)} KB/s | ETA: ${(p.estimatedTime / 1000).toFixed(0)}s`);
});
download.on('finish', (info) => {
console.log(`Saved to ${info.fileName} (${info.fileSize} bytes)`);
});
download.on('error', (err) => {
console.error('Download failed:', err.message);
}); Method Signature
rezo.download(url: string | URL, saveTo: string, options?: RezoHttpRequest): RezoDownloadResponse - url — the resource to download.
- saveTo — local file path where the file will be saved.
- options — optional request configuration (headers, auth, proxy, timeout, etc.).
The method returns immediately. The download runs asynchronously and emits events as it progresses.
Events Reference
initiated
Emitted when the request object has been created:
download.on('initiated', () => {
console.log('Download request created');
}); start
Emitted when the request is sent to the server:
download.on('start', (info) => {
console.log('Downloading from:', info.url);
console.log('Method:', info.method);
}); headers
Emitted when response headers arrive (first byte):
download.on('headers', (info) => {
console.log('Status:', info.status, info.statusText);
console.log('Content-Type:', info.contentType);
console.log('File size:', info.contentLength, 'bytes');
console.log('TTFB:', info.timing.firstByte, 'ms');
}); status
Emitted with the HTTP status code:
download.on('status', (status, statusText) => {
if (status !== 200) {
console.warn(`Unexpected status: ${status} ${statusText}`);
}
}); cookies
Emitted with cookies from the response:
download.on('cookies', (cookies) => {
cookies.forEach(c => console.log(`${c.key}=${c.value}`));
}); redirect
Emitted when a redirect is followed:
download.on('redirect', (info) => {
console.log(`Redirect: ${info.sourceUrl} -> ${info.destinationUrl}`);
}); progress
Emitted periodically as data is received:
download.on('progress', (progress) => {
console.log(`Downloaded: ${progress.loaded} / ${progress.total} bytes`);
console.log(`Percentage: ${progress.percentage.toFixed(1)}%`);
console.log(`Speed: ${(progress.speed / 1024 / 1024).toFixed(2)} MB/s`);
console.log(`Average speed: ${(progress.averageSpeed / 1024 / 1024).toFixed(2)} MB/s`);
console.log(`ETA: ${(progress.estimatedTime / 1000).toFixed(1)} seconds`);
}); The progress object contains:
| Property | Type | Description |
|---|---|---|
loaded | number | Bytes received so far |
total | number | Total bytes (from Content-Length) |
percentage | number | 0—100 |
speed | number | Current speed in bytes/second |
averageSpeed | number | Average speed in bytes/second |
estimatedTime | number | Estimated time remaining in milliseconds |
timestamp | number | Timestamp of this progress event |
finish / done
Emitted when the download completes. Both events carry the same payload:
download.on('finish', (info) => {
console.log('File saved:', info.fileName);
console.log('File size:', info.fileSize, 'bytes');
console.log('Status:', info.status, info.statusText);
console.log('Final URL:', info.finalUrl);
console.log('Total time:', info.timing.total, 'ms');
console.log('Download time:', info.timing.download, 'ms');
console.log('Average speed:', (info.averageSpeed / 1024 / 1024).toFixed(2), 'MB/s');
console.log('Cookies:', info.cookies.string);
console.log('URLs traversed:', info.urls);
}); done is an alias for finish.
error
Emitted if the download fails:
download.on('error', (err) => {
console.error('Download error:', err.code, err.message);
}); Progress Tracking Patterns
Console Progress Bar
const download = rezo.download('https://releases.example.com/large-file.tar.gz', './large-file.tar.gz');
download.on('headers', (info) => {
const sizeMB = ((info.contentLength || 0) / 1024 / 1024).toFixed(1);
console.log(`Starting download: ${sizeMB} MB`);
});
download.on('progress', (p) => {
const bar = '#'.repeat(Math.floor(p.percentage / 2)).padEnd(50, '-');
const speedMB = (p.speed / 1024 / 1024).toFixed(1);
const etaSec = (p.estimatedTime / 1000).toFixed(0);
process.stdout.write(`
[${bar}] ${p.percentage.toFixed(0)}% | ${speedMB} MB/s | ETA: ${etaSec}s`);
});
download.on('finish', (info) => {
const totalMB = (info.fileSize / 1024 / 1024).toFixed(1);
const totalSec = (info.timing.total / 1000).toFixed(1);
console.log(`
Complete: ${totalMB} MB in ${totalSec}s`);
}); Collecting Timing Metadata
download.on('finish', (info) => {
const report = {
url: info.finalUrl,
file: info.fileName,
size: info.fileSize,
dns: info.timing.dns,
tcp: info.timing.tcp,
tls: info.timing.tls,
ttfb: info.timing.firstByte,
download: info.timing.download,
total: info.timing.total,
avgSpeed: info.averageSpeed
};
saveMetrics(report);
}); Filename Tracking
The DownloadResponse exposes the file name and URL:
const download = rezo.download('https://cdn.example.com/assets/report.pdf', '/tmp/report.pdf');
console.log(download.fileName); // '/tmp/report.pdf'
console.log(download.url); // 'https://cdn.example.com/assets/report.pdf' After the download finishes, info.fileName in the finish event contains the final path.
With Request Options
Pass any standard request option as the third argument:
const download = rezo.download(
'https://private-cdn.example.com/build-artifact.tar.gz',
'./artifact.tar.gz',
{
headers: {
'Authorization': 'Bearer token123',
'Accept-Encoding': 'gzip'
},
timeout: 120_000,
maxRedirects: 5,
proxy: 'http://proxy.example.com:8080'
}
); Checking State
download.isFinished(); // true after finish event Error Handling
When a download fails mid-transfer, the adapter cleans up the partial file. Listen for the error event to handle failures:
download.on('error', (err) => {
console.error(`Download of ${download.url} failed: ${err.message}`);
// The partial file is cleaned up automatically
}); For retries, create a new download call:
async function downloadWithRetry(url, saveTo, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const dl = rezo.download(url, saveTo);
await new Promise((resolve, reject) => {
dl.on('finish', resolve);
dl.on('error', reject);
});
return; // Success
} catch (err) {
if (attempt === maxRetries) throw err;
console.log(`Retry ${attempt}/${maxRetries}...`);
}
}
}