express-fileupload
Advanced tools
Comparing version 1.1.4 to 1.1.5
'use strict'; | ||
const fileFactory = require('./fileFactory'); | ||
const processNested = require('./processNested'); | ||
const {buildOptions} = require('./utilities'); | ||
const processMultipart = require('./processMultipart'); | ||
const isEligibleRequest = require('./isEligibleRequest'); | ||
const processNested = require('./processNested'); | ||
const {buildOptions} = require('./utilities'); | ||
const fileUploadOptionsDefaults = { | ||
const DEFAULT_OPTIONS = { | ||
fileHandler: false, | ||
uriDecodeFileNames: false, | ||
safeFileNames: false, | ||
@@ -23,11 +25,10 @@ preserveExtension: false, | ||
* Expose the file upload middleware | ||
* @param {Object} options - Middleware options. | ||
* @returns {Function} | ||
*/ | ||
module.exports = (fileUploadOptions) => { | ||
fileUploadOptions = buildOptions(fileUploadOptionsDefaults, fileUploadOptions); | ||
module.exports = (options) => { | ||
const fileUploadOptions = buildOptions(DEFAULT_OPTIONS, options); | ||
return function(req, res, next){ | ||
if (!isEligibleRequest(req)) { | ||
return next(); | ||
} | ||
return (req, res, next) => { | ||
if (!isEligibleRequest(req)) return next(); | ||
processMultipart(fileUploadOptions, req, res, next); | ||
@@ -34,0 +35,0 @@ }; |
@@ -16,3 +16,3 @@ const crypto = require('crypto'); | ||
const getBuffer = () => Buffer.concat(buffers); | ||
const getBuffer = () => Buffer.concat(buffers, fileSize); | ||
const emptyFunc = () => ''; | ||
@@ -19,0 +19,0 @@ |
@@ -10,3 +10,4 @@ const Busboy = require('busboy'); | ||
buildFields, | ||
parseFileName | ||
parseFileName, | ||
uriDecodeFileName | ||
} = require('./utilities'); | ||
@@ -35,4 +36,6 @@ | ||
// Build req.files fields | ||
busboy.on('file', (fieldname, file, filename, encoding, mime) => { | ||
busboy.on('file', (fieldname, file, name, encoding, mime) => { | ||
// Decode file name if uriDecodeFileNames option set true. | ||
const filename = uriDecodeFileName(options, name); | ||
const {dataHandler, getFilePath, getFileSize, getHash, complete, cleanup} = options.useTempFiles | ||
@@ -39,0 +42,0 @@ ? tempFileHandler(options, fieldname, filename) |
@@ -36,4 +36,3 @@ 'use strict'; | ||
prefix = prefix || TEMP_PREFIX; | ||
tempCounter ++; | ||
if (tempCounter > TEMP_COUNTER_MAX) tempCounter = 1; | ||
tempCounter = tempCounter >= TEMP_COUNTER_MAX ? 1 : tempCounter + 1; | ||
return `${prefix}-${tempCounter}-${Date.now()}`; | ||
@@ -59,17 +58,11 @@ }; | ||
const promiseCallback = (resolve, reject) => { | ||
return (err) => { | ||
if (err) { | ||
errorFunc(resolve, reject)(err); | ||
} else { | ||
resolve(); | ||
} | ||
}; | ||
return err => err ? errorFunc(resolve, reject)(err) : resolve(); | ||
}; | ||
/** | ||
* Builds instance options from arguments objects. | ||
* Builds instance options from arguments objects(can't be arrow function). | ||
* @returns {Object} - result options. | ||
*/ | ||
const buildOptions = function(){ | ||
let result = {}; | ||
const result = {}; | ||
[...arguments].forEach(options => { | ||
@@ -90,6 +83,4 @@ if (!options || typeof options !== 'object') return; | ||
const buildFields = (instance, field, value) => { | ||
//Do nothing if value is not set. | ||
if (value === null || value === undefined){ | ||
return instance; | ||
} | ||
// Do nothing if value is not set. | ||
if (value === null || value === undefined) return instance; | ||
instance = instance || {}; | ||
@@ -101,3 +92,3 @@ // Non-array fields | ||
// Array fields | ||
if (instance[field] instanceof Array){ | ||
if (instance[field] instanceof Array) { | ||
instance[field].push(value); | ||
@@ -115,13 +106,15 @@ } else { | ||
* @param {String} filePath | ||
* @returns {Boolean} | ||
*/ | ||
const checkAndMakeDir = function(fileUploadOptions, filePath){ | ||
//Check upload options were set. | ||
const checkAndMakeDir = (fileUploadOptions, filePath) => { | ||
// Check upload options were set. | ||
if (!fileUploadOptions) return false; | ||
if (!fileUploadOptions.createParentPath) return false; | ||
//Check whether folder for the file exists. | ||
// Check whether folder for the file exists. | ||
if (!filePath) return false; | ||
const parentPath = path.dirname(filePath); | ||
//Create folder if it is not exists. | ||
if (!fs.existsSync(parentPath)) fs.mkdirSync(parentPath); | ||
return true; | ||
// Create folder if it is not exists. | ||
if (!fs.existsSync(parentPath)) fs.mkdirSync(parentPath, { recursive: true }); | ||
// Checks folder again and return a results. | ||
return fs.existsSync(parentPath); | ||
}; | ||
@@ -133,11 +126,3 @@ | ||
*/ | ||
const deleteFile = (file, callback) => { | ||
fs.unlink(file, (err) => { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
callback(); | ||
}); | ||
}; | ||
const deleteFile = (file, callback) => fs.unlink(file, err => err ? callback(err) : callback()); | ||
@@ -149,4 +134,4 @@ /** | ||
*/ | ||
const copyFile = function(src, dst, callback){ | ||
//cbCalled flag and runCb helps to run cb only once. | ||
const copyFile = (src, dst, callback) => { | ||
// cbCalled flag and runCb helps to run cb only once. | ||
let cbCalled = false; | ||
@@ -158,6 +143,6 @@ let runCb = (err) => { | ||
}; | ||
//Create read stream | ||
// Create read stream | ||
let readable = fs.createReadStream(src); | ||
readable.on('error', runCb); | ||
//Create write stream | ||
// Create write stream | ||
let writable = fs.createWriteStream(dst); | ||
@@ -169,3 +154,3 @@ writable.on('error', (err)=>{ | ||
writable.on('close', () => runCb()); | ||
//Copy file via piping streams. | ||
// Copy file via piping streams. | ||
readable.pipe(writable); | ||
@@ -181,11 +166,4 @@ }; | ||
const moveFile = (src, dst, callback) => { | ||
// Copy file to dst. | ||
copyFile(src, dst, (err) => { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
// Delete src file. | ||
deleteFile(src, callback); | ||
}); | ||
// Copy file to dst and delete src whether success. | ||
copyFile(src, dst, err => err ? callback(err) : deleteFile(src, callback)); | ||
}; | ||
@@ -198,19 +176,18 @@ | ||
*/ | ||
const saveBufferToFile = function(buffer, filePath, callback){ | ||
if (!Buffer.isBuffer(buffer)){ | ||
callback(new Error('buffer variable should be a Buffer!')); | ||
return; | ||
const saveBufferToFile = (buffer, filePath, callback) => { | ||
if (!Buffer.isBuffer(buffer)) { | ||
return callback(new Error('buffer variable should be type of Buffer!')); | ||
} | ||
//Setup readable stream from buffer. | ||
// Setup readable stream from buffer. | ||
let streamData = buffer; | ||
let readStream = Readable(); | ||
readStream._read = ()=>{ | ||
readStream._read = () => { | ||
readStream.push(streamData); | ||
streamData = null; | ||
}; | ||
//Setup file system writable stream. | ||
// Setup file system writable stream. | ||
let fstream = fs.createWriteStream(filePath); | ||
fstream.on('error', error => callback(error)); | ||
fstream.on('close', () => callback()); | ||
//Copy file via piping streams. | ||
// Copy file via piping streams. | ||
readStream.pipe(fstream); | ||
@@ -220,2 +197,11 @@ }; | ||
/** | ||
* Decodes uriEncoded file names. | ||
* @param fileName {String} - file name to decode. | ||
* @returns {String} | ||
*/ | ||
const uriDecodeFileName = (opts, fileName) => { | ||
return opts.uriDecodeFileNames ? decodeURIComponent(fileName) : fileName; | ||
}; | ||
/** | ||
* Parses filename and extension and returns object {name, extension}. | ||
@@ -227,16 +213,12 @@ * @param preserveExtension {Boolean, Integer} - true/false or number of characters for extension. | ||
const parseFileNameExtension = (preserveExtension, fileName) => { | ||
let preserveExtensionLengh = parseInt(preserveExtension); | ||
let result = {name: fileName, extension: ''}; | ||
if (!preserveExtension && preserveExtensionLengh !== 0){ | ||
return result; | ||
} | ||
const preserveExtensionLengh = parseInt(preserveExtension); | ||
const result = {name: fileName, extension: ''}; | ||
if (!preserveExtension && preserveExtensionLengh !== 0) return result; | ||
// Define maximum extension length | ||
let maxExtLength = isNaN(preserveExtensionLengh) | ||
const maxExtLength = isNaN(preserveExtensionLengh) | ||
? MAX_EXTENSION_LENGTH | ||
: Math.abs(preserveExtensionLengh); | ||
let nameParts = fileName.split('.'); | ||
if (nameParts.length < 2) { | ||
return result; | ||
} | ||
const nameParts = fileName.split('.'); | ||
if (nameParts.length < 2) return result; | ||
@@ -266,7 +248,5 @@ let extension = nameParts.pop(); | ||
const parseFileName = (opts, fileName) => { | ||
if (!opts.safeFileNames) { | ||
return fileName; | ||
} | ||
if (!opts.safeFileNames) return fileName; | ||
// Set regular expression for the file name. | ||
let safeNameRegex = typeof opts.safeFileNames === 'object' && opts.safeFileNames instanceof RegExp | ||
const nameRegex = typeof opts.safeFileNames === 'object' && opts.safeFileNames instanceof RegExp | ||
? opts.safeFileNames | ||
@@ -276,5 +256,5 @@ : SAFE_FILE_NAME_REGEX; | ||
let {name, extension} = parseFileNameExtension(opts.preserveExtension, fileName); | ||
if (extension.length) extension = '.' + extension.replace(safeNameRegex, ''); | ||
if (extension.length) extension = '.' + extension.replace(nameRegex, ''); | ||
return name.replace(safeNameRegex, '').concat(extension); | ||
return name.replace(nameRegex, '').concat(extension); | ||
}; | ||
@@ -295,3 +275,4 @@ | ||
parseFileName, | ||
getTempFilename | ||
getTempFilename, | ||
uriDecodeFileName | ||
}; |
{ | ||
"name": "express-fileupload", | ||
"version": "1.1.4", | ||
"version": "1.1.5", | ||
"author": "Richard Girges <richardgirges@gmail.com>", | ||
@@ -5,0 +5,0 @@ "description": "Simple express file upload middleware that wraps around Busboy", |
@@ -79,2 +79,3 @@ # express-fileupload | ||
createParentPath | <ul><li><code>false</code> **(default)**</li><li><code>true</code></ul> | Automatically creates the directory path specified in `.mv(filePathName)` | ||
uriDecodeFileNames | <ul><li><code>false</code> **(default)**</li><li><code>true</code></ul> | Applies uri decoding to file names if set true. | ||
safeFileNames | <ul><li><code>false</code> **(default)**</li><li><code>true</code></li><li>regex</li></ul> | Strips characters from the upload's filename. You can use custom regex to determine what to strip. If set to `true`, non-alphanumeric characters _except_ dashes and underscores will be stripped. This option is off by default.<br /><br />**Example #1 (strip slashes from file names):** `app.use(fileUpload({ safeFileNames: /\\/g }))`<br />**Example #2:** `app.use(fileUpload({ safeFileNames: true }))` | ||
@@ -81,0 +82,0 @@ preserveExtension | <ul><li><code>false</code> **(default)**</li><li><code>true</code></li><li><code>*Number*</code></li></ul> | Preserves filename extension when using <code>safeFileNames</code> option. If set to <code>true</code>, will default to an extension length of 3. If set to <code>*Number*</code>, this will be the max allowable extension length. If an extension is smaller than the extension length, it remains untouched. If the extension is longer, it is shifted.<br /><br />**Example #1 (true):**<br /><code>app.use(fileUpload({ safeFileNames: true, preserveExtension: true }));</code><br />*myFileName.ext* --> *myFileName.ext*<br /><br />**Example #2 (max extension length 2, extension shifted):**<br /><code>app.use(fileUpload({ safeFileNames: true, preserveExtension: 2 }));</code><br />*myFileName.ext* --> *myFileNamee.xt* |
@@ -229,2 +229,7 @@ 'use strict'; | ||
}); | ||
// | ||
it('checkAndMakeDir creates a dir recursively if path to the file not exists', ()=>{ | ||
let dir = path.join(uploadDir, 'testfolder', 'testsubfolder', 'testfile'); | ||
assert.equal(checkAndMakeDir({createParentPath: true}, dir), true); | ||
}); | ||
}); | ||
@@ -362,2 +367,26 @@ //saveBufferToFile tests | ||
}); | ||
describe('Test uriDecodeFileName function', function() { | ||
const testData = [ | ||
{ enc: 'test%22filename', dec: 'test"filename' }, | ||
{ enc: 'test%60filename', dec: 'test`filename' }, | ||
{ enc: '%3Fx%3Dtest%22filename', dec: '?x=test"filename'} | ||
]; | ||
// Test decoding if uriDecodeFileNames: true. | ||
testData.forEach((testName) => { | ||
const opts = { uriDecodeFileNames: true }; | ||
it(`Return ${testName.dec} for input ${testName.enc} if uriDecodeFileNames: true`, () => { | ||
assert.equal(uriDecodeFileName(opts, testName.enc), testName.dec); | ||
}); | ||
}); | ||
// Test decoding if uriDecodeFileNames: false. | ||
testData.forEach((testName) => { | ||
const opts = { uriDecodeFileNames: false }; | ||
it(`Return ${testName.enc} for input ${testName.enc} if uriDecodeFileNames: false`, () => { | ||
assert.equal(uriDecodeFileName(opts, testName.enc), testName.enc); | ||
}); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
1209695
2349
94
0