nodejs-file-downloader
Advanced tools
Comparing version 4.10.6 to 4.11.0
@@ -0,1 +1,6 @@ | ||
## 4.11.0 02/04/2023 | ||
### Added | ||
- Added support for Data URI | ||
## 4.10.6 07/01/2023 | ||
@@ -2,0 +7,0 @@ |
259
Download.js
@@ -10,13 +10,15 @@ const fs = require('fs'); | ||
const FileProcessor = require('./utils/FileProcessor'); | ||
const pipelinePromisified = util.promisify(stream.pipeline); | ||
const mkdir = util.promisify(fs.mkdir); | ||
const writeFile = util.promisify(fs.writeFile); | ||
const { deduceFileName, exists } = require('./utils/fileName'); | ||
const { deduceFileName, exists, getTempFilePath } = require('./utils/fileName'); | ||
const { isJson } = require('./utils/string'); | ||
const { isDataUrl } = require('./utils/url'); | ||
const unlink = util.promisify(fs.unlink) | ||
const rename = util.promisify(fs.rename) | ||
const { bufferToReadableStream, createWriteStream, createBufferFromResponseStream, pipeStreams, getStringFromStream } = require('./utils/stream'); | ||
const downloadStatusEnum = { | ||
COMPLETE:'COMPLETE', | ||
ABORTED:"ABORTED" | ||
COMPLETE: 'COMPLETE', | ||
ABORTED: "ABORTED" | ||
} | ||
@@ -88,10 +90,8 @@ | ||
if (this.config.fileName && this.config.skipExistingFileName) { | ||
if (await exists(this.config.directory + '/' + this.config.fileName)) { | ||
return { downloadStatus: downloadStatusEnum.ABORTED, filePath: null } | ||
} | ||
if(await this._shouldSkipRequest()){ | ||
return { downloadStatus: downloadStatusEnum.ABORTED, filePath: null } | ||
} | ||
try { | ||
const { dataStream, originalResponse } = await this._request(); | ||
@@ -101,9 +101,4 @@ | ||
if (originalResponse.statusCode > 226) { | ||
await this._handlePossibleStatusCodeError({dataStream, originalResponse}) | ||
const error = await this._createErrorObject(dataStream, originalResponse) | ||
throw error; | ||
} | ||
if (this.config.onResponse) { | ||
@@ -117,5 +112,7 @@ | ||
let { finalFileName, originalFileName } = await this._getFileName(originalResponse.headers); | ||
const finalPath = await this._save({ dataStream, originalResponse }) | ||
return { filePath:finalPath, downloadStatus: finalPath ? downloadStatusEnum.COMPLETE : downloadStatusEnum.ABORTED} | ||
const finalPath = await this._getFinalPath({dataStream, finalFileName, originalFileName}) | ||
return { filePath: finalPath, downloadStatus: finalPath ? downloadStatusEnum.COMPLETE : downloadStatusEnum.ABORTED } | ||
} catch (error) { | ||
@@ -133,4 +130,29 @@ | ||
async _shouldSkipRequest() { | ||
if (this.config.fileName && this.config.skipExistingFileName) { | ||
if (await exists(this.config.directory + '/' + this.config.fileName)) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
/** | ||
* | ||
* @param {Object} obj | ||
* @param {stream.Readable} obj.dataStream | ||
* @param {http.IncomingMessage} obj.originalResponse | ||
*/ | ||
async _handlePossibleStatusCodeError({dataStream, originalResponse}){ | ||
if (originalResponse.statusCode > 226) { | ||
const error = await this._createErrorObject(dataStream, originalResponse) | ||
throw error; | ||
} | ||
} | ||
async _createErrorObject(dataStream, originalResponse) { | ||
const responseString = await this._getStringFromStream(dataStream); | ||
const responseString = await getStringFromStream(dataStream); | ||
@@ -149,10 +171,4 @@ const error = new Error(`Request failed with status code ${originalResponse.statusCode}`) | ||
async _getStringFromStream(stream) { | ||
const buffer = await this._createBufferFromResponseStream(stream); | ||
return buffer.toString(); | ||
} | ||
/** | ||
@@ -167,3 +183,2 @@ * | ||
/** | ||
@@ -173,41 +188,113 @@ * @return {Promise<{dataStream:stream.Readable,originalResponse:IncomingMessage}} | ||
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 } | ||
if (isDataUrl(this.config.url)) { | ||
return this._mimic_RequestForDataUrl(this.config.url); | ||
} | ||
else { | ||
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 }; | ||
} | ||
} | ||
/** | ||
* @param {string} dataUrl | ||
* @return {Promise<{dataStream:stream.Readable,originalResponse:IncomingMessage}} | ||
*/ | ||
_mimic_RequestForDataUrl(dataUrl) { | ||
const mimeType = dataUrl.match(/data:([^;]+);/)[1]; | ||
const base64Data = dataUrl.replace(/^data:[^;]+;base64,/, ''); | ||
const data = Buffer.from(base64Data, 'base64'); | ||
const dataStream = bufferToReadableStream(data); | ||
const originalResponse = new IncomingMessage(null); | ||
originalResponse.headers = { | ||
'content-type': mimeType, | ||
'content-length': data.byteLength | ||
}; | ||
originalResponse.statusCode = 200; | ||
this.fileSize = data.byteLength; | ||
return { dataStream, originalResponse }; | ||
} | ||
async _getFinalPath({ dataStream, finalFileName, originalFileName }) { | ||
let finalPath | ||
const shouldSkipSaving = await this._shouldSkipSaving(originalFileName) | ||
if (!shouldSkipSaving) { | ||
finalPath = await this._save({ dataStream, finalFileName, originalFileName }); | ||
} else { | ||
finalPath = null | ||
} | ||
return finalPath | ||
} | ||
/** | ||
* @param {Promise<{dataStream:stream.Readable,originalResponse:IncomingMessage}} | ||
* @return {Promise<string | null>} finalPath | ||
* | ||
* @param {string} originalFileName | ||
* @returns | ||
*/ | ||
async _save({ dataStream, originalResponse }) { | ||
async _shouldSkipSaving(originalFileName) { | ||
if (this.config.skipExistingFileName && await exists(this.config.directory + '/' + originalFileName)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
/** | ||
* | ||
* @param {string} finalFileName | ||
* @returns {{finalPath:string,tempPath:string}} | ||
*/ | ||
_getTempAndFinalPath(finalFileName) { | ||
const finalPath = `${this.config.directory}/${finalFileName}`; | ||
var tempPath = getTempFilePath(finalPath); | ||
return { finalPath, tempPath } | ||
} | ||
async _saveAccordingToConfig({ dataStream, tempPath }) { | ||
if (this.config.shouldBufferResponse) { | ||
const buffer = await createBufferFromResponseStream(dataStream); | ||
await this._saveFromBuffer(buffer, tempPath); | ||
} else { | ||
await this._saveFromReadableStream(dataStream, tempPath); | ||
} | ||
} | ||
async _handleOnBeforeSave(finalFileName) { | ||
if (this.config.onBeforeSave) { | ||
const clientOverideName = await this.config.onBeforeSave(finalFileName) | ||
if (clientOverideName && typeof clientOverideName === 'string') { | ||
finalFileName = clientOverideName; | ||
} | ||
} | ||
return finalFileName | ||
} | ||
/** | ||
* @param {Promise<{dataStream:stream.Readable,finalFileName:string,originalFileName:string}} | ||
* @return {Promise<string>} finalPath | ||
*/ | ||
async _save({ dataStream, finalFileName, originalFileName }) { | ||
try { | ||
let { finalFileName, originalFileName } = await this._getFileName(originalResponse.headers); | ||
if (this.config.skipExistingFileName && await exists(this.config.directory + '/' + originalFileName)) { | ||
// will skip this request | ||
if (await this._shouldSkipSaving(originalFileName)) { | ||
return null; | ||
} | ||
if (this.config.onBeforeSave) { | ||
const clientOverideName = await this.config.onBeforeSave(finalFileName) | ||
if (clientOverideName && typeof clientOverideName === 'string') { | ||
finalFileName = clientOverideName; | ||
} | ||
} | ||
finalFileName = await this._handleOnBeforeSave(finalFileName) | ||
const finalPath = `${this.config.directory}/${finalFileName}`; | ||
const { finalPath, tempPath } = this._getTempAndFinalPath(finalFileName) | ||
var tempPath = this._getTempFilePath(finalPath); | ||
await this._saveAccordingToConfig({ dataStream, tempPath }) | ||
if (this.config.shouldBufferResponse) { | ||
const buffer = await this._createBufferFromResponseStream(dataStream); | ||
await this._saveFromBuffer(buffer, tempPath); | ||
} else { | ||
await this._saveFromReadableStream(dataStream, tempPath); | ||
} | ||
await this._renameTempFileToFinalName(tempPath, finalPath) | ||
@@ -223,10 +310,5 @@ | ||
} | ||
} | ||
/** | ||
@@ -254,31 +336,5 @@ * | ||
return { dataStream, originalResponse } | ||
} | ||
} | ||
/** | ||
* | ||
* @param {string} fullPath | ||
* @return {Promie<WritableStream>} | ||
*/ | ||
_createWriteStream(fullPath) { | ||
return fs.createWriteStream(fullPath) | ||
} | ||
/** | ||
* | ||
* @param {stream.Readable} stream | ||
* @returns | ||
*/ | ||
async _createBufferFromResponseStream(stream) { | ||
const chunks = [] | ||
for await (let chunk of stream) { | ||
chunks.push(chunk) | ||
} | ||
const buffer = Buffer.concat(chunks) | ||
return buffer; | ||
} | ||
_getProgressStream() { | ||
@@ -313,18 +369,7 @@ const that = this; | ||
} | ||
async _pipeStreams(arrayOfStreams) { | ||
await pipelinePromisified(...arrayOfStreams); | ||
} | ||
async _saveFromReadableStream(read, path) { | ||
const streams = [read]; | ||
const write = this._createWriteStream(path) | ||
const write = createWriteStream(path) | ||
if (this.config.onProgress) { | ||
@@ -336,9 +381,6 @@ const progressStream = this._getProgressStream() | ||
streams.push(write) | ||
await this._pipeStreams(streams) | ||
await pipeStreams(streams) | ||
} | ||
async _saveFromBuffer(buffer, path) { | ||
@@ -357,13 +399,4 @@ await writeFile(path, buffer) | ||
/** | ||
* | ||
* @param {string} finalpath | ||
*/ | ||
_getTempFilePath(finalpath) { | ||
return `${finalpath}.download`; | ||
} | ||
/** | ||
* @param {object} responseHeaders | ||
@@ -398,7 +431,3 @@ */ | ||
} | ||
} | ||
} | ||
} |
@@ -72,3 +72,2 @@ const rpur = require('./utils/rpur') | ||
// super(); | ||
if (!config || typeof config !== 'object') { | ||
@@ -138,3 +137,2 @@ throw new Error('Must provide a valid config object') | ||
debugger; | ||
@@ -162,3 +160,2 @@ } | ||
// console.log('e from shouldstop',e) | ||
if (e.code === 'ERR_REQUEST_CANCELLED')//Means the request was cancelled, therefore no repetition is required. | ||
@@ -165,0 +162,0 @@ return true; |
@@ -15,2 +15,42 @@ const expect = require('expect') | ||
it('Should download a picture from data URI', async () => { | ||
const downloader = new Downloader({ | ||
url: `data:image/jpeg;base64,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`, | ||
directory: "./downloads", | ||
onProgress: (p, chunk) => { | ||
expect(p).toBe('100.00') | ||
}, | ||
onResponse: (r) => { | ||
expect(r.constructor.name).toBe('IncomingMessage'); | ||
} | ||
}) | ||
const { downloadStatus, filePath } = await downloader.download(); | ||
expect(downloadStatus).toBe('COMPLETE') | ||
expect(filePath).toBe('./downloads/9k=.jpeg') | ||
await verifyFile(filePath, 2339 ); | ||
}) | ||
it('Should download a picture from data URI, with custom name', async () => { | ||
const downloader = new Downloader({ | ||
fileName:"buba.jpeg", | ||
url: `data:image/jpeg;base64,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`, | ||
directory: "./downloads", | ||
onProgress: (p, chunk) => { | ||
expect(p).toBe('100.00') | ||
}, | ||
onResponse: (r) => { | ||
expect(r.constructor.name).toBe('IncomingMessage'); | ||
}, | ||
onBeforeSave: (r) => { | ||
return r.split(".")[0]+"2" + ".jpeg" | ||
} | ||
}) | ||
const { downloadStatus, filePath } = await downloader.download(); | ||
expect(downloadStatus).toBe('COMPLETE') | ||
expect(filePath).toBe('./downloads/buba2.jpeg') | ||
await verifyFile(filePath, 2339 ); | ||
}) | ||
it('Should download a picture and use content-type', async () => { | ||
@@ -45,2 +85,4 @@ const host = randomHost() | ||
it('Should get the deduced name', async () => { | ||
@@ -47,0 +89,0 @@ let deducedName; |
@@ -103,3 +103,2 @@ const { http, https } = require('follow-redirects'); | ||
const majorNodeVersion = process.versions.node.split('.')[0]; | ||
// debugger | ||
if (!majorNodeVersion || majorNodeVersion < 14) { | ||
@@ -106,0 +105,0 @@ request.abort() |
{ | ||
"name": "nodejs-file-downloader", | ||
"version": "4.10.6", | ||
"version": "4.11.0", | ||
"description": "A file downloader for NodeJs", | ||
@@ -5,0 +5,0 @@ "main": "Downloader.js", |
@@ -5,9 +5,5 @@ const sanitize = require('sanitize-filename'); | ||
const { promises: Fs } = require('fs') | ||
const crypto = require('crypto'); | ||
/** | ||
@@ -36,3 +32,2 @@ * | ||
const fileNameFromContentDisposition = getFileNameFromContentDisposition(headers['content-disposition'] || headers['Content-Disposition']); | ||
// console.log('filenamecontentdisposition', fileNameFromContentDisposition) | ||
if (fileNameFromContentDisposition) return fileNameFromContentDisposition; | ||
@@ -64,4 +59,3 @@ | ||
// var contentType = this.response.headers['content-type'] || this.response.headers['Content-Type']; | ||
// console.log(contentType) | ||
let extension = mime.extension(contentType) | ||
@@ -109,2 +103,13 @@ | ||
module.exports = { deduceFileName, exists } | ||
/** | ||
* | ||
* @param {string} finalpath | ||
*/ | ||
function getTempFilePath(finalpath) { | ||
return `${finalpath}.download`; | ||
} | ||
module.exports = { deduceFileName, exists,getTempFilePath } |
const path = require('path'); | ||
const fs = require('fs'); | ||
// const { resolve } = require('path'); | ||
class FileProcessor { | ||
constructor(config) { | ||
this.originalFileName = config.fileName; | ||
@@ -16,6 +14,6 @@ this.fileExtension = path.extname(this.originalFileName); | ||
this.useSynchronousMode = config.useSynchronousMode || false; | ||
} | ||
getAvailableFileName() { | ||
@@ -40,3 +38,3 @@ if (this.useSynchronousMode) { | ||
pathExistsSync(path){ | ||
pathExistsSync(path) { | ||
return fs.existsSync(path); | ||
@@ -47,27 +45,7 @@ } | ||
pathExists(path) { | ||
// debugger; | ||
if(this.useSynchronousMode){ | ||
// console.log('path',path) | ||
if (this.useSynchronousMode) { | ||
return this.pathExistsSync(path); | ||
} | ||
// console.log('useSynchronousMode',this.useSynchronousMode) | ||
// return new Promise((resolve, reject) => { | ||
// fs.open(path, 'r', (err) => { | ||
// // debugger; | ||
// if (err) { | ||
// if (err.code === 'ENOENT') { | ||
// // console.error('myfile does not exist'); | ||
// return resolve(false); | ||
// } | ||
// reject(err); | ||
// } | ||
// resolve(true); | ||
// }); | ||
// }) | ||
return new Promise((resolve,reject)=>{ | ||
return new Promise((resolve, reject) => { | ||
fs.access(path, (err) => { | ||
@@ -81,4 +59,2 @@ if (err) { | ||
}) | ||
} | ||
@@ -90,3 +66,3 @@ | ||
if (!this.pathExistsSync(this.basePath +fileName)) { | ||
if (!this.pathExistsSync(this.basePath + fileName)) { | ||
return fileName; | ||
@@ -96,3 +72,3 @@ } | ||
counter = counter + 1; | ||
let newFileName = this.fileNameWithoutExtension + "_"+ counter + this.fileExtension; | ||
let newFileName = this.fileNameWithoutExtension + "_" + counter + this.fileExtension; | ||
@@ -112,3 +88,3 @@ return this.createNewFileNameSync(newFileName, counter); | ||
counter = counter + 1; | ||
let newFileName = this.fileNameWithoutExtension + "_"+ counter + this.fileExtension; | ||
let newFileName = this.fileNameWithoutExtension + "_" + counter + this.fileExtension; | ||
@@ -120,35 +96,4 @@ return await this.createNewFileName(newFileName, counter); | ||
// fileNameExistsSync(fileName) { | ||
// if (this.useSynchronousMode) { | ||
// return this.pathExists(this.basePath + fileName); | ||
// } | ||
// } | ||
// fileNameExists(fileName) { | ||
// return new Promise((resolve, reject) => { | ||
// fs.open(this.basePath + fileName, 'r', (err) => { | ||
// debugger; | ||
// if (err) { | ||
// if (err.code === 'ENOENT') { | ||
// // console.error('myfile does not exist'); | ||
// return resolve(false); | ||
// } | ||
// reject(err); | ||
// } | ||
// resolve(true); | ||
// }); | ||
// }) | ||
// } | ||
} | ||
module.exports = FileProcessor; |
@@ -16,12 +16,6 @@ const {createDelay} = require('./delay') | ||
*/ | ||
// async function repeatPromiseUntilResolved(...args) {//Destructuring arguments in order to avoid having the "attempts" counter as part of the API. | ||
module.exports = async function rpur(promiseFactory,config={}) { | ||
// const promiseFactory = args[0] | ||
// const config = args[1] | ||
// const attempts = args[2] || 0 | ||
// debugger; | ||
const attempts = arguments[2] || 0 | ||
// console.log(attempts) | ||
// const {maxRetries} = config | ||
// debugger; | ||
const dummy = () => false; | ||
@@ -35,18 +29,11 @@ const shouldStop = config.shouldStop || dummy; | ||
// console.log('Attempt number: ',attempts+1) | ||
if (config.onAttempt) { | ||
await config.onAttempt(attempts + 1) | ||
} | ||
// debugger; | ||
const promise = promiseFactory(); | ||
const result = await promiseWithTimeout(promise, timeout); | ||
// const result = await promiseFactory(); | ||
return result; | ||
} catch (error) { | ||
// debugger; | ||
// console.log('Retrying failed promise'); | ||
// const newAttempts = attempts + 1; | ||
if (config.onError) { | ||
// debugger; | ||
await config.onError(error, newAttempts) | ||
@@ -58,7 +45,4 @@ } | ||
throw error; | ||
// console.log('Attempts', newAttempts) | ||
// if (newAttempts == maxAttempts) {//If it reached the maximum allowed number of retries, it throws an error. | ||
// throw error; | ||
// } | ||
if (delay) { | ||
@@ -76,7 +60,5 @@ await createDelay(delay); | ||
function promiseWithTimeout(promise, time) { | ||
// debugger; | ||
return new Promise(async (resolve, reject) => { | ||
if (time) { | ||
var timeout = setTimeout(() => { | ||
// console.log('timed out!') | ||
reject(new Error('Promise timed out as defined in the config')) | ||
@@ -83,0 +65,0 @@ }, time) |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
200311
24
2098
8