Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nodejs-file-downloader

Package Overview
Dependencies
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nodejs-file-downloader - npm Package Compare versions

Comparing version 4.5.1 to 4.5.2

CHANGELOG.md

263

Download.js
const fs = require('fs');
// const abort = require('./utils/abort')
const http = require('http')//For jsdoc
const IncomingMessage = http.IncomingMessage
// const ClientRequest = http.ClientRequest
const makeRequest = require('./makeRequest');
const stream = require('stream');

@@ -16,3 +14,2 @@ var HttpsProxyAgent = require('https-proxy-agent');

const { deduceFileName } = require('./utils/fileName');
const RequestWrapper = require('./RequestWrapper');
const unlink = util.promisify(fs.unlink)

@@ -64,16 +61,9 @@ const rename = util.promisify(fs.rename)

this.wrapperReject = null;//A reference to the reject function of the wrapping promise;
this.wrapperPromise = null;
this.wrapperPromiseRejected = false;
this.saveStreamPromise = null;
// this.isCancelled = false;
this.isCancelled = false;
this.cancelCb = null;//Function from makeRequest, to cancel the download.
this.percentage = 0;
this.fileSize = null;
this.currentDataSize = 0;
this.originalResponse = null;//The IncomingMessage read stream.
/**
* @property {RequestWrapper} requestWrapper
*/
this.requestWrapper = null;

@@ -84,4 +74,4 @@ }

/**
* The entire download process.
* @return {Promise<void>}

@@ -91,72 +81,37 @@ */

const prom = new Promise(async (resolve, reject) => {
// debugger
this.wrapperReject = reject;
await this._verifyDirectoryExists(this.config.directory)
try {
const { dataStream, originalResponse } = await this._request();
this.originalResponse = originalResponse;
try {
await this._verifyDirectoryExists(this.config.directory)
// debugger
// if (this.config.onBeforeRequest) {
// await this.config.onBeforeRequest();
// }
this._makeRequest();
const response = await this._awaitResponse()
if (this.config.onResponse) {
if (this.config.onResponse) {
// debugger
const shouldContinue = await this.config.onResponse(response);
if (shouldContinue === false) {
return resolve();
}
const shouldContinue = await this.config.onResponse(originalResponse);
if (shouldContinue === false) {
return;
}
// debugger
await this._save(response)
// debugger
}
await this._save({ dataStream, originalResponse })
} catch (error) {
resolve();
// debugger
} catch (error) {
// debugger
if (!this.wrapperPromiseRejected) {//If the request was cancelled, ignore any error.
this.reject(error);
}
if (this.isCancelled) {
const customError = new Error('Request cancelled')
customError.code = 'ERR_REQUEST_CANCELLED'
throw customError
}
})
throw error;
}
// this.wrapperPromise = prom;
return prom;
}
// debugger
/**
*
* @returns {Promise<IncomingMessage>} response
* @param {string} directory
*/
async _awaitResponse() {
// debugger
const response = await this.requestWrapper.getResponse()
// debugger
const headers = response.headers;
// debugger
const contentLength = headers['content-length'] || headers['Content-Length'];
this.fileSize = parseInt(contentLength);
return response;
async _verifyDirectoryExists(directory) {
await mkdir(directory, { recursive: true });
}

@@ -166,66 +121,24 @@

/**
*
* @returns {Promise<void>}
* @return {Promise<{dataStream:stream.Readable,originalResponse:IncomingMessage}}
*/
_makeRequest() {
const { timeout, headers, proxy, url, httpsAgent } = this.config;
const options = {
timeout,
headers
}
if (httpsAgent) {
options.httpsAgent = httpsAgent;
}
else if (proxy) {
// debugger
options.httpsAgent = new HttpsProxyAgent(proxy)
}
async _request() {
const { dataStream, originalResponse } = await this._makeRequest();
const headers = originalResponse.headers;
const contentLength = headers['content-length'] || headers['Content-Length'];
this.fileSize = parseInt(contentLength);
return { dataStream, originalResponse }
// debugger
const wrapper = new RequestWrapper(url, options)
this.requestWrapper = wrapper;
wrapper.makeRequest()
// this.responsePromise = wrapper.responsePromise;
// this.requestWrapper = request;
this._setEvents()
}
_setEvents() {
this.requestWrapper.onError((e) => {
// debugger
// console.log(e)
this.reject(e)
})
if (this.config.timeout)
this.requestWrapper.setTimeout(this.config.timeout, () => {
this.abort()
const error = new Error(`Request timed out`)
error.code = "ERR_REQUEST_TIMEDOUT"
this.reject(new Error(error))
})
}
/**
* @param {IncomingMessage} response
* @param {Promise<{dataStream:stream.Readable,originalResponse:IncomingMessage}}
* @return {Promise<void>}
*/
async _save(response) {
async _save({ dataStream, originalResponse }) {
try {
// debugger
let finalName = await this._getFinalFileName(response.headers);
let finalName = await this._getFinalFileName(originalResponse.headers);
if (this.config.onBeforeSave) {
// debugger
const clientOverideName = await this.config.onBeforeSave(finalName)

@@ -242,13 +155,7 @@ if (clientOverideName && typeof clientOverideName === 'string') {

if (this.config.shouldBufferResponse) {
// debugger
const buffer = await this._createBufferFromResponseStream(response);
// debugger
if (this.wrapperPromiseRejected) {
return await this._removeFailedFile(tempPath)
}
const buffer = await this._createBufferFromResponseStream(dataStream);
await this._saveFromBuffer(buffer, tempPath);
// await this._saveFromBuffer(buffer, finalPath);
} else {
await this._saveFromReadableStream(response, tempPath);
await this._saveFromReadableStream(dataStream, tempPath);
// await this._saveFromReadableStream(response, finalPath);

@@ -261,3 +168,5 @@ }

// debugger
await this._removeFailedFile(tempPath)
if (!this.config.shouldBufferResponse)
await this._removeFailedFile(tempPath)
throw error;

@@ -268,4 +177,35 @@ }

}
async _verifyDirectoryExists(directory) {
await mkdir(directory, { recursive: true });
/**
*
* @return {Promise<{dataStream:stream.Readable,originalResponse:IncomingMessage}}
*/
async _makeRequest() {
const { timeout, headers, proxy, url, httpsAgent } = this.config;
const options = {
timeout,
headers
}
if (httpsAgent) {
options.httpsAgent = httpsAgent;
}
else if (proxy) {
// debugger
options.httpsAgent = new HttpsProxyAgent(proxy)
}
// debugger
// const { response, request } = await makeRequest(url, options);
const { makeRequestIter, cancel } = makeRequest(url, options)
// debugger
this.cancelCb = cancel
const { dataStream, originalResponse, } = await makeRequestIter()
// debugger
return { dataStream, originalResponse }
}

@@ -287,15 +227,11 @@

*
* @param {IncomingMessage} stream
* @param {stream.Readable} stream
* @returns
*/
async _createBufferFromResponseStream(stream) {
// debugger
const chunks = []
for await (let chunk of stream) {
// if(this.wrapperPromiseRejected){
// throw new Error('Stream interrupted')
// }
chunks.push(chunk)
}
// debugger
const buffer = Buffer.concat(chunks)

@@ -324,3 +260,2 @@ return buffer;

if (that.config.onProgress) {
// debugger
that.config.onProgress(that.percentage, chunk, remainingSize);

@@ -345,11 +280,3 @@ }

async _pipeStreams(arrayOfStreams) {
try {
// debugger
await pipelinePromisified(...arrayOfStreams);
// debugger
} catch (error) {
// debugger
throw error;
}
await pipelinePromisified(...arrayOfStreams);
}

@@ -360,3 +287,2 @@

async _saveFromReadableStream(read, path) {
// debugger;
const streams = [read];

@@ -370,5 +296,3 @@ const write = this._createWriteStream(path)

streams.push(write)
// debugger
await this._pipeStreams(streams)
// debugger

@@ -381,4 +305,2 @@

async _saveFromBuffer(buffer, path) {
// debugger;
// const tempPath = this._getTempFilePath(path);
await writeFile(path, buffer)

@@ -428,36 +350,11 @@

/**
* Reject the wrapping promise of the entire download process
* @param {Error} e
*/
reject(e) {
// debugger
this.wrapperPromiseRejected = true;
this.wrapperReject(e)
}
async abort() {
this.requestWrapper.abort();
}
/**
* Cancels the download, and rejects the wrapping promise
*/
cancel() {
// debugger
if (this.cancelCb) {
this.isCancelled = true;
// this.isCancelled = true;
// const request = this.requestWrapper.getRequest()
// abort(request)
if (this.requestWrapper)
this.abort()
this.cancelCb()
}
const customError = new Error('Request cancelled')
customError.code = 'ERR_REQUEST_CANCELLED'
// debugger
this.reject(customError)
}

@@ -464,0 +361,0 @@ }

@@ -28,28 +28,3 @@

mandatory: false
},
onProgress: {
type: 'function',
mandatory: false
},
onResponse: {
type: 'function',
mandatory: false
},
shouldStop: {
type: 'function',
mandatory: false
},
onBeforeSave: {
type: 'function',
mandatory: false
},
onError: {
type: 'function',
mandatory: false
},
maxAttempts: {
type: 'number',
mandatory: false
},
}
};

@@ -149,10 +124,7 @@

const that = this;
const { url, directory, fileName, cloneFiles, timeout, headers, httpsAgent, proxy, onResponse, onBeforeSave, onProgress, shouldBufferResponse, useSynchronousMode } = that.config;
const { url, directory, fileName, cloneFiles, timeout, headers, httpsAgent, proxy, onResponse, onBeforeSave, onProgress, shouldBufferResponse, useSynchronousMode } = that.config;
//Repeat downloading process until success
await that._makeUntilSuccessful(async () => {
const download = new Download({ url, directory, fileName, cloneFiles, timeout, headers, httpsAgent, proxy, onResponse, onBeforeSave, onProgress, shouldBufferResponse, useSynchronousMode });
const download = new Download({ url, directory, fileName, cloneFiles, timeout, headers, httpsAgent, proxy, onResponse, onBeforeSave, onProgress, shouldBufferResponse, useSynchronousMode });
this._currentDownload = download

@@ -185,3 +157,3 @@

// console.log('e from shouldstop',e)
if (e.code === 'ERR_REQUEST_CANCELLED')//Means the request was cancelled, therefore no repetition is required.

@@ -188,0 +160,0 @@ return true;

{
"name": "nodejs-file-downloader",
"version": "4.5.1",
"version": "4.5.2",
"description": "A file downloader for NodeJs",
"main": "Downloader.js",
"scripts": {
"test": "mocha Downloader.test.js",
"test": "mocha *.test.js",
"test-timeout": "mocha timeout-cancellation.test.js",
"test-watch": "nodemon --exec \"npm test\""

@@ -34,3 +35,4 @@ },

"nock": "^13.0.4",
"rimraf": "^3.0.2"
"rimraf": "^3.0.2",
"supertest": "^6.1.3"
},

@@ -37,0 +39,0 @@ "homepage": "https://github.com/ibrod83/nodejs-file-downloader",

@@ -20,3 +20,2 @@ 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.

* [Prevent unnecessary repetition](#prevent-unnecessary-repetition)
* [Cancel a download](#cancel-a-download)
* [Use a Proxy](#use-a-proxy)

@@ -238,40 +237,2 @@ - [Error handling](#error-handling)

#### Cancel a download
This feature is new. Kindly report any bugs you encounter.
Useful for Electron apps.
```javascript
const downloader = new Downloader({
url: 'http://212.183.159.230/200MB.zip',
directory: "./",
})
try {
//Mocking cancellation
setTimeout(()=>{
downloader.cancel()
},2000)
await downloader.download();
//If the download is cancelled, the promise will not be resolved, so this part is never reached
console.log('done');
} catch (error) {//When the download is cancelled, 'ERR_REQUEST_CANCELLED' error is thrown. This is how you can handle cancellations in your code.
if(error.code === 'ERR_REQUEST_CANCELLED'){
//do something after cancellation..
}else{
//handle general error..
}
}
```
&nbsp;
#### Use a proxy

@@ -278,0 +239,0 @@

@@ -16,3 +16,3 @@

// res.send('Hello World!')
const read = fs.createReadStream('./fixtures/Desert.jpg');
const read = fs.createReadStream('./fixtures/Desert.jpg',{highWaterMark:2000});
// read.pipe(res);

@@ -41,2 +41,5 @@

app.get('/cancelBeforeStream', async (req, res) => {

@@ -70,3 +73,3 @@ // console.log('incoming request!')

app.get('/timeout', (req, res) => {
app.get('/timeoutDuringStream', (req, res) => {
// console.log('incoming request!')

@@ -113,2 +116,19 @@ // res.send('Hello World!')

app.get('/timeoutBeforeResponse', async(req, res) => {
// console.log('incoming request!')
// res.send('Hello World!')
const read = fs.createReadStream('./fixtures/Koala.jpg',{highWaterMark:2000});
// read.pipe(res);
await timeout(5000)
const modifiedStream = Readable.from((async function* () {
for await (const chunk of read) {
yield chunk
}
})());
modifiedStream.pipe(res);
})
const server = app.listen(port, () => {

@@ -134,1 +154,46 @@ console.log(port)

// async function* sourceData() {
// yield 1;
// yield 2;
// yield 3;
// }
// async function* filter(source, predicate) {
// for await (const item of source) {
// if (await predicate(item)) {
// yield item;
// }
// }
// }
// async function* map(source, mapper) {
// for await (const item of source) {
// yield await mapper(item);
// }
// }
// let iter = sourceData();
// iter = filter(iter, async val => await checkNumIsValidViaExternalService(val));
// iter = map(iter, val => val.toFixed(2));
// iter = map(iter, async val => {
// await new Promise(resolve => setTimeout(resolve, 100));
// return val;
// });
// const finalIter = sourceData()
// .filter(...)
// .map(...)
// .map(...)
// const finalIter = pipe(
// sourceData(),
// filter(...),
// map(...),
// map(...)
// );
// for await (const item of iter) {
// console.log(item);
// }
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc