nodejs-file-downloader is a simple utility for downloading files. It hides the complexity of dealing with streams, redirects, paths and duplicate file names. Can automatically repeat failed downloads.
If you encounter any bugs or have a question, please don't hesitate to open an issue.
Installation
$ npm install nodejs-file-downloader
Table of Contents
Examples
Basic
Download a large file with default configuration
const Downloader = require("nodejs-file-downloader");
(async () => {
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./downloads",
});
try {
await downloader.download();
console.log("All done");
} catch (error) {
console.log("Download failed", error);
}
})();
Get the progress of a download
If the response headers contain information about the file size, onProgress hook can be used. If the file size cannot be determined, "percentage" and "remainingSize" arguments will be called with NaN.
const Downloader = require("nodejs-file-downloader");
(async () => {
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./downloads/2020/May",
onProgress: function (percentage, chunk, remainingSize) {
console.log("% ", percentage);
console.log("Current chunk of data: ", chunk);
console.log("Remaining bytes: ", remainingSize);
},
});
try {
await downloader.download();
} catch (error) {
console.log(error);
}
})();
Get the deduced file name and override it
If you haven't supplied a "fileName" config property, the program will try its best to choose the most appropriate name for the file(from the URL, headers, etc). In case you wish to know the name that was chosen, before the file is actually saved, use the onBeforeSave hook, which is called with the deduced name.
If you're "unhappy" with the name, it can be overridden by returning a new string. Returning anything else(including undefined/void), will
let the program know that it can keep the name.
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./downloads/2020/May",
onBeforeSave: (deducedName) => {
console.log(`The file name is: ${deducedName}`);
},
});
Custom file name
Normally, nodejs-file-downloader "deduces" the file name, from the URL or the response headers. If you want to choose a custom file name, supply a config.fileName property.
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./downloads/2020/May",
fileName: "somename.zip",
});
Overwrite existing files
By default, nodejs-file-downloader uses config.cloneFiles = true, which means that files with an existing name, will have a number appended to them.
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./",
cloneFiles: false,
});
If you want to completely skip downloading a file, when a file with the same name already exists, use config.skipExistingFileName = true
Just add a "headers" property to the config object:
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./",
headers: {
"Some-Header": value,
},
});
Hook into response
If you need to get the underlying response, in order to decide whether the download should continue, or perform any other operations, use the onReponse hook.
function onResponse(response) {
if (response.headers["content-length"] > 1000000) {
console.log("File is too big!");
return false;
}
}
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./",
onResponse,
});
try {
await downloader.download();
} catch (error) {
console.log(error);
}
Repeat failed downloads automatically
The program can repeat any failed downloads automatically. Only if the provided config.maxAttempts number is exceeded, an Error is thrown.
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./",
maxAttempts: 3,
onError: function (error) {
console.log("Error from attempt ", error);
},
});
try {
await downloader.download();
} catch (error) {
console.log("Final fail", error);
}
Prevent unnecessary repetition
If you use the auto-repeat option, by setting the maxAttempts to greater than 1, "shouldStop" hook can be used, To decide
whether the repetition should continue. This is useful in cases where you're generating many dynamic url's, some of which can result in a 404 http status code(for example), and there is no point in repeating that request.
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./",
maxAttempts: 3,
shouldStop: function (error) {
if (e.statusCode && e.statusCode === 404) {
return true;
}
},
});
try {
await downloader.download();
} catch (error) {
console.log("Final fail", error);
}
Cancel a download
This feature is new. Kindly report any bugs you encounter.
Useful for Electron apps.
const downloader = new Downloader({
url: "http://212.183.159.230/200MB.zip",
directory: "./",
});
try {
setTimeout(() => {
downloader.cancel();
}, 2000);
await downloader.download();
console.log("done");
} catch (error) {
if (error.code === "ERR_REQUEST_CANCELLED") {
} else {
}
}
Use a proxy
You can pass a proxy string. Under the hood, this will create a custom httpsAgent. This feature wasn't tested extensively.
const downloader = new Downloader({
proxy: "http://username:password@some-proxy.com:22225",
url: "http://212.183.159.230/200MB.zip",
directory: "./",
});
Error handling
downloader.download() will throw an error, just like Axios, in case of network problems
or an http status code of 400 or higher.
If the auto-repeat feature is enabled(by setting the maxAttempts to higher than 1), then only a failure of the final attempt will throw an error.
Getting the response object during an error
If a status code of 400 or above is received, the program throws an error. In this case, a reference to Node's response object(http.IncomingMessage) will be appended to the
Error object.This is useful only in cases where the server might send back JSON or HTML, instead of the requested file, with some custom error handling on the response body.
It can be accessed like this:
const downloader = new Downloader({
url: "http://www.somesite.com/400",
directory: "./",
});
try {
await downloader.download();
} catch (error) {
if (error.response) {
const response = error.response;
let str = "";
response.on("data", function (chunk) {
str += chunk;
});
response.on("end", function () {
console.log(str)
});
}
}