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

multer-gridfs-storage

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

multer-gridfs-storage - npm Package Compare versions

Comparing version 1.1.1 to 1.2.0

8

CHANGELOG.md

@@ -0,1 +1,7 @@

1.2.0
=====
* Added generator function support
* Allow to use promises in configuration options instead of callbacks
1.1.1

@@ -6,2 +12,3 @@ =====

1.1.0

@@ -13,2 +20,3 @@ ==================

* Allow the api to be called with the `new` operator
* Added Typescript support

@@ -15,0 +23,0 @@ 1.0.3

@@ -0,1 +1,8 @@

/**
*
* Module entry point
* @module multer-gridfs-storage
*
*/
'use strict';

@@ -5,2 +12,10 @@

/**
* Storage constructor function
* @name GridFSStorage
* @type class
* @static
* @see [GridFSStorage]{@link module:multer-gridfs-storage/gridfs~GridFSStorage}
* **/
module.exports = GridFsStorage;

377

lib/gridfs.js

@@ -0,1 +1,7 @@

/**
*
* Plugin definition
* @module multer-gridfs-storage/gridfs
*
*/
'use strict';

@@ -8,95 +14,67 @@

var util = require('util');
var storageUtils = require('./utils');
var __ = require('./utils');
var noop = storageUtils.noop;
var getFilename = storageUtils.getFilename;
var logMessage = storageUtils.logMessage;
var isFunction = storageUtils.isFunction;
var generateValue = storageUtils.generateValue;
var validateOptions = storageUtils.validateOptions;
/**
* @class GridFSStorage
* @classdesc Multer GridFS Storage Engine class definition.
* @extends EventEmitter
* @param {object} opts
* @param {string} opts.url - The url pointing to a MongoDb database
* @param {Grid | Promise} opts.gfs - The Grid instance to use or
* @param {Function} [opts.filename] - A function to control the file naming in the database
* @param {Function} [opts.identifier] - A function to control the unique identifier of the file
* @param {Function} [opts.metadata] - A function to control the metadata object associated to the file
* @param {Function} [opts.chunkSize] - The preferred size of file chunks in bytes
* @param {string | Function} [opts.root] - The root collection to store the files
* @param {boolean | Function} [opts.log=false] - Enable or disable logging
* @param {string} [opts.logLevel='file'] - The events to be logged out
* @fires GridFSStorage#connection
* @fires GridFSStorage#file
* @version 0.0.3
*/
function GridFSStorage(opts) {
var self = this;
if (!(self instanceof GridFSStorage)) {
if (!(this instanceof GridFSStorage)) {
return new GridFSStorage(opts);
}
EventEmitter.call(self);
EventEmitter.call(this);
validateOptions(opts);
self.gfs = null;
self._log = opts.log || false;
self._logLevel = opts.logLevel || 'file';
self._getIdentifier = opts.identifier || noop;
self._getFilename = opts.filename || getFilename;
self._getMetadata = opts.metadata || noop;
if (opts.chunkSize && isFunction(opts.chunkSize)) {
self._getChunkSize = opts.chunkSize;
} else {
self._getChunkSize = generateValue(opts.chunkSize ? opts.chunkSize : 261120);
}
__.validateOptions(opts);
if (opts.root) {
if (isFunction(opts.root)) {
self._getRoot = opts.root;
} else {
self._getRoot = generateValue(opts.root);
}
} else {
self._getRoot = noop;
}
if (!opts.gfs) {
mongo.MongoClient.connect(opts.url, function (err, db) {
var gfs;
if (err) {
throw err;
}
function errEvent(err) {
logMessage(self, err, true);
}
gfs = new Grid(db, mongo);
if (self._logLevel === 'all') {
logMessage(self, { message: 'MongoDb connected in url ' + opts.url });
db.on('close', function () {
logMessage(self, 'Disconnected from MongoDb database', true);
});
db.on('error', errEvent).on('parseError', errEvent).on('timeout', errEvent);
}
self.gfs = gfs;
self.emit('connection', self.gfs, self.gfs.db);
});
} else {
if ('then' in opts.gfs) {
var promise = opts.gfs;
promise
.then(function (gfs) {
self.gfs = gfs;
self.emit('connection', self.gfs, self.gfs.db);
})
.then(null, function (err) {
logMessage(self, err, true);
// There is no need to rethrow because the promise is not returned.
// We are just adding a new handler to the received promise
// so any handlers in user code will execute as well
});
} else {
self.gfs = opts.gfs;
self.emit('connection', self.gfs, self.gfs.db);
}
}
this.gfs = null;
this._configure(opts);
this._connect(opts);
}
/**
* Event emmited when the Storage is instantiated with the `url` option
* @event module:multer-gridfs-storage/gridfs~GridFSStorage#connection
* @param {Grid} gfs - The created gfs instance
* @param {MongoDb} db - The MongoDb database used to create the grid instance
*
*/
/**
* Event emmited when a new file is uploaded
* @event module:multer-gridfs-storage/gridfs~GridFSStorage#file
* @param {File} file - The uploaded file
*
*/
util.inherits(GridFSStorage, EventEmitter);
/**
* Storage interface method to handle incoming files
* @function _handleFile
* @instance
* @memberOf module:multer-gridfs-storage/gridfs~GridFSStorage
* @param {Express.Request} req - The request that trigger the upload
* @param {Multer.File} file - The uploaded file stream
* @param {function} cb - A standard node callback to signal the end of the upload or an error
*
* */
GridFSStorage.prototype._handleFile = function _handleFile(req, file, cb) {
var self = this;
var streamOpts = { content_type: file.mimetype };
self._getChunkSize(req, file, function (err, chunkSize) {
self._generate('_getChunkSize', req, file, function (err, chunkSize) {
if (err) {

@@ -106,3 +84,3 @@ return cb(err);

streamOpts.chunkSize = chunkSize;
self._getRoot(req, file, function (err, root) {
self._generate('_getRoot', req, file, function (err, root) {
if (err) {

@@ -112,3 +90,3 @@ return cb(err);

streamOpts.root = root;
self._getIdentifier(req, file, function (err, id) {
self._generate('_getIdentifier', req, file, function (err, id) {
if (err) {

@@ -121,3 +99,3 @@ return cb(err);

self._getFilename(req, file, function (err, filename) {
self._generate('_getFilename', req, file, function (err, filename) {
if (err) {

@@ -127,3 +105,5 @@ return cb(err);

streamOpts.filename = filename;
self._getMetadata(req, file, function (err, metadata) {
self._generate('_getMetadata', req, file, function (err, metadata) {
var writeStream;
if (err) {

@@ -134,10 +114,10 @@ return cb(err);

var writestream = self.gfs.createWriteStream(streamOpts);
writeStream = self.gfs.createWriteStream(streamOpts);
file.stream.pipe(writestream);
writestream.on('error', cb);
file.stream.pipe(writeStream);
writeStream.on('error', cb);
writestream.on('close', function (f) {
writeStream.on('close', function (f) {
self.emit('file', f);
logMessage(self, { message: 'saved file', extra: f });
self._logMessage({ message: 'Saved file', extra: f });
cb(null, {

@@ -158,18 +138,227 @@ filename: filename,

/**
* Storage interface method to delete files in case an error turns the request invalid
* @function _removeFile
* @instance
* @memberOf module:multer-gridfs-storage/gridfs~GridFSStorage
* @param {Express.Request} req - The request that trigger the upload
* @param {Multer.File} file - The uploaded file stream
* @param {function} cb - A standard node callback to signal the end of the upload or an error
*
* */
GridFSStorage.prototype._removeFile = function _removeFile(req, file, cb) {
function onRemove(err) {
if (err) {
return cb(err);
}
this._logMessage({ message: 'Deleted file ', extra: file });
return cb(null);
}
if (file.grid) {
this.gfs.remove({ _id: file.id }, onRemove.bind(this));
} else {
return cb(null);
}
};
/**
* Tests for generator functions or plain functions and delegates to the apropiate method
* @function _generate
* @instance
* @memberOf module:multer-gridfs-storage/gridfs~GridFSStorage
* @param {string} method - The internal function name that should be executed
* @param {Express.Request} req - The request that trigger the upload as received in _handleFile
* @param {Multer.File} file - The uploaded file stream as received in _handleFile
* @param {function} cb - A standard node callback to signal the end of the upload or an error as received in _handleFile
*
* */
GridFSStorage.prototype._generate = function _generate(method, req, file, cb) {
var result, generator;
try {
if (__.isGeneratorFunction(this[method])) {
generator = this[method](req, file);
// Should we store a reference?
//this[method + 'Ref'] = this[method];
this[method] = generator;
result = generator.next();
this._handleResult(result, cb, true);
} else if (__.isGenerator(this[method])) {
generator = this[method];
result = generator.next([req, file]);
this._handleResult(result, cb, true);
} else {
result = this[method](req, file, cb);
this._handleResult(result, cb, false);
}
} catch (e) {
return cb(e, null);
}
};
/**
* Handles generator function and promise results
* @function _handleResult
* @instance
* @memberOf module:multer-gridfs-storage/gridfs~GridFSStorage
* @param {object} result - Can be a promise or a generator yielded value
* @param {function} cb - A standard node callback to signal the end of the upload or an error as received in _handleFile
* @param {boolean} isGen - True if is a yielded value
*
* */
GridFSStorage.prototype._handleResult = function (result, cb, isGen) {
var value = result;
function onFullfill(data) {
cb(null, data);
}
function onReject(err) {
cb(err, null);
}
if (isGen) {
if (result.done) {
throw new Error('Generator ended unexpectedly');
}
value = result.value;
}
if (__.isPromise(value)) {
value.then(onFullfill, onReject);
} else if (isGen) {
return cb(null, value);
}
};
/**
* Handles optional configuration properties
* @function _configure
* @instance
* @memberOf module:multer-gridfs-storage/gridfs~GridFSStorage
* @param {object} opts - Configuration object passed in the constructor
*
* */
GridFSStorage.prototype._configure = function (opts) {
this._log = opts.log || false;
this._logLevel = opts.logLevel || 'file';
this._getIdentifier = opts.identifier || __.noop;
this._getFilename = opts.filename || __.getFilename;
this._getMetadata = opts.metadata || __.noop;
if (opts.chunkSize && __.isFuncOrGeneratorFunc(opts.chunkSize)) {
this._getChunkSize = opts.chunkSize;
} else {
this._getChunkSize = __.generateValue(opts.chunkSize ? opts.chunkSize : 261120);
}
if (opts.root) {
if (__.isFuncOrGeneratorFunc(opts.root)) {
this._getRoot = opts.root;
} else {
this._getRoot = __.generateValue(opts.root);
}
} else {
this._getRoot = __.noop;
}
};
/**
* Handles connection settings and emits connection event
* @function _connect
* @instance
* @memberOf module:multer-gridfs-storage/gridfs~GridFSStorage
* @param {object} opts - Configuration object passed in the constructor
*
* */
GridFSStorage.prototype._connect = function (opts) {
var self = this;
if (file.grid) {
self.gfs.remove({ _id: file.id }, function (err) {
var promise;
function onFullfill(gfs) {
self.gfs = gfs;
self.emit('connection', self.gfs, self.gfs.db);
}
function onReject(err) {
self._logError(err);
}
if (!opts.gfs) {
mongo.MongoClient.connect(opts.url, function (err, db) {
var gfs;
if (err) {
cb(err);
throw err;
}
logMessage(self, { message: 'Deleted file ', extra: file });
cb(null);
function errEvent(err) {
// Needs verification. Sometimes the event fires without an error object
// although the docs specify each of the events has a MongoError argument
self._logError(err || new Error());
}
gfs = new Grid(db, mongo);
if (self._logLevel === 'all') {
self._logMessage({ message: 'MongoDb connected in url ' + opts.url, extra: db });
// This are all the events that emit errors
db.on('error', errEvent)
.on('parseError', errEvent)
.on('timeout', errEvent)
.on('close', errEvent);
}
self.gfs = gfs;
self.emit('connection', self.gfs, self.gfs.db);
});
} else {
cb(null);
if ('then' in opts.gfs) {
promise = opts.gfs;
promise.then(onFullfill, onReject);
} else {
self.gfs = opts.gfs;
self.emit('connection', self.gfs, self.gfs.db);
}
}
};
/**
* Logs messages or errors
* Use the console if enabled or the passed function in the constructor `log` option
* @function _logMessage
* @instance
* @memberOf module:multer-gridfs-storage/gridfs~GridFSStorage
* @param {object | string | Error} log - The object or error to log
* @param {boolean} error - If true logs the message as an error
*
* */
GridFSStorage.prototype._logMessage = function _logMessage(log, error) {
var method = error ? 'error' : 'log';
function logConsole() {
/*eslint-disable no-console */
console[method](log.message, log.extra);
/*eslint-enable no-console */
}
var logFn = this._log === true ? logConsole : this._log;
if (logFn) {
if (error) {
return logFn(log, null);
}
logFn(null, log);
}
};
/**
* Logs errors only. Uses `_logMessage` under the hood.
* @function _logError
* @instance
* @memberOf module:multer-gridfs-storage/gridfs~GridFSStorage
* @param {string | Error} err - The error to log
*
* */
GridFSStorage.prototype._logError = function (err) {
this._logMessage(err, true);
};
module.exports = GridFSStorage;

@@ -0,1 +1,7 @@

/**
*
* Utility functions
* @module multer-gridfs-storage/utils
*
* */
'use strict';

@@ -6,9 +12,68 @@

/**
* Returns the ES6 built-in tag of the tested value (The internal [[Class]] slot in ES5)
* @function builtinTag
* @inner
* @param {object} target - The object to be tested
* @since 1.1.0
* @returns {string} The built in tag of the target
*
* */
function builtinTag(target) {
return Object.prototype.toString.call(target);
}
/**
* Test a value to see if is a Promise
* @function isPromise
* @static
* @param {object} target - The object to be tested
* @since 1.1.0
* @returns {boolean} Returns true if the target parameter is a thenable (Promsie)
*
* */
function isPromise(target) {
// Promise A+ spec - 1.1
// "Promise is an object or function with a then method"
// The spec also specifies that it must conform to a certain behavior but this is impossible to check by just inspecting the target
// So we can only check if is a valid thenable
return target !== null && (typeof target === 'object' || isFunction(target)) && isFunction(target.then);
}
/**
* Test a value to see if is a Promise or a Grid instance
* @function isGfsOrPromise
* @static
* @param {object} target - The object to be tested
* @since 1.1.0
* @returns {boolean} Returns true if the target parameter is a promise or an instance of gridfs-stream
* */
function isGfsOrPromise(target) {
return target instanceof Grid || ((typeof target === 'object' || isFunction(target)) && 'then' in target);
return target instanceof Grid || isPromise(target);
}
/**
* Test a value to see if is a function or a generator function
* @function isFuncOrGeneratorFunc
* @static
* @param {object} target - The object to be tested
* @since 1.1.0
* @returns {boolean} Returns true if the target parameter is a function or a generator function
* */
function isFuncOrGeneratorFunc(target) {
return isFunction(target) || isGeneratorFunction(target);
}
/**
* Generates a random string to use as the filename
* @function getFileName
* @static
* @param {Request} req - A reference to the request object
* @param {File} file - A reference to the file being uploaded
* @param {Function} cb - A callback function to return the name used
* @since 1.1.0
* */
function getFileName(req, file, cb) {
var randomBytes = crypto.randomBytes || crypto.pseudoRandomBytes;
randomBytes(16, function (err, buffer) {

@@ -19,2 +84,9 @@ cb(err, err ? null : buffer.toString('hex'));

/**
* Generate any fixed value using a callback
* @function generateValue
* @static
* @param {any} value - A function to generate callbacks that invokes with a given value
* @since 1.1.0
* */
function generateValue(value) {

@@ -26,2 +98,11 @@ return function getValue(req, file, cb) {

/**
* Generates a null value using a callback
* @function noop
* @static
* @param {Request} req - A reference to the request object
* @param {File} file - A reference to the file being uploaded
* @param {Function} cb - A callback function to return the name used
* @since 1.1.0
* */
function noop(req, file, cb) {

@@ -31,63 +112,308 @@ cb(null, null);

/**
* Test a value to see if is a plain function
* Return false for proxies, generator functions and async functions
* @function isFunction
* @static
* @param {any} target - The value to test
* @returns {boolean} Returns true if the target parameter is a function
* @since 1.1.0
* */
function isFunction(target) {
// taken from: lodash - isFunction method
// https://github.com/lodash/lodash/blob/master/isFunction.js
// lodash also returns true for Proxy, async and generator functions which is not the desired behavior in this case
var type = typeof target;
var mayBeFunc = (type === 'object' || type === 'function');
var objClass = Object.prototype.toString.call(target);
return target !== null && mayBeFunc && objClass === '[object Function]';
return target !== null && mayBeFunc && builtinTag(target) === '[object Function]';
}
function logMessage(instance, log, error) {
var method = error ? 'error' : 'log';
function logConsole() {
/*eslint-disable no-console */
console[method](log.message, log.extra);
/*eslint-enable no-console */
/**
* Test a value to see if is a plain object
* Return false for any "object" like value that is not created with an Object or null prototype
* @function isObject
* @static
* @param {any} target - The value to test
* @returns {boolean} Returns true if the target parameter is an object
* @since 1.2.0
* */
function isObject(target) {
// taken from: lodash - isPlainObject method
// https://github.com/lodash/lodash/blob/master/isPlainObject.js
// avoid having an extra dependency
var notAnObject, proto, toStr, Ctor;
notAnObject = target === null || typeof target !== 'object' || builtinTag(target) !== '[object Object]';
if (notAnObject) {
return false;
}
var logFn = instance._log === true ? logConsole : instance._log;
if (logFn) {
if (error) {
return logFn(log, null);
proto = Object.getPrototypeOf(target);
// true for Object.create(null);
if (proto === null) {
return true;
}
toStr = Function.prototype.toString;
Ctor = Object.prototype.hasOwnProperty.call(proto, 'constructor') && proto.constructor;
return proto.hasOwnProperty('constructor') && typeof Ctor === 'function' &&
Ctor instanceof Ctor && toStr.call(Object) === toStr.call(proto.constructor);
}
/**
* Test an object to see if it has all of the given properties
* @function hasProps
* @static
* @param {any} target - The value to test
* @param {array} props - An array of property names that should be present in the target
* @returns {boolean} Return false if any of the properties is missing
* @since 1.2.0
* */
function hasProps(target, props) {
var prop, i;
var valid = false;
if (typeof props === 'string') {
valid = props in target;
} else {
for (i = 0; i < props.length; i++) {
prop = props[i];
if (prop in target) {
valid = true;
break;
}
}
logFn(null, log);
}
return valid;
}
// TODO: Use joi or similar module to validate input
function validateOptions(opts) {
var i, prop, fnOpts, type;
if (!opts || (!('url' in opts) && !('gfs' in opts))) {
throw new Error('Missing required configuration');
/**
* Checks if a value is a generator function
* @function isGeneratorFunction
* @static
* @param {any} target - The value to test
* @returns {boolean} Return true if the target is a generator function
* @since 1.2.0
* @todo Support polyfills which are plain functions that returns a generator like object
* */
function isGeneratorFunction(target) {
return builtinTag(target) === '[object GeneratorFunction]';
}
/**
* Checks if a value is a generator
* @function isGenerator
* @static
* @param {any} target - The value to test
* @returns {boolean} Return true only if the target is a generator created as the result of invoking a generator function
* @since 1.2.0
* @todo Support polyfills
* */
function isGenerator(target) {
if (target === null || target === undefined) {
return false;
}
if ('gfs' in opts) {
if (!isGfsOrPromise(opts.gfs)) {
throw new Error('Expected gfs configuration to be a Grid instance or a promise');
return builtinTag(target) === '[object Generator]';
/*
// Since this module only accepts generator functions as input (and not generator objects) we can safely disable the other checks
// These might be required for polyfills
var isGen = builtinTag(target) === '[object Generator]';
if (isGen) {
return true;
}
var isSymbolSupported = !!global.Symbol;
if (isSymbolSupported) {
var iteratorProp = target[Symbol.iterator];
var isIterable = isFunction(iteratorProp) && iteratorProp.length === 0;
if (!isIterable) {
return false;
}
// In some implementations if called as iteratorProp() throws an error because `this` is undefined
var iterator = target[Symbol.iterator]();
// Make sure is a well-formed iterable
// The Iterator spec says `next` is "suposed" to be called without arguments but they might receive some
// Doesn't specify how many
// The generator spec specifies at most one
// Eg:
// generator.next(value);
// ...Inside the generator
// var parameter = yield nextValue; // parameter === value
return iterator !== null && iterator !== undefined && isFunction(iterator.next) && isFunction(iterator.return) && isFunction(iterator.throw);
}
if ('logLevel' in opts && !(opts.logLevel === 'file' || opts.logLevel === 'all')) {
throw new Error('Invalid log level configuration. Must be either "file" or "all"');
return false;
*/
}
/**
* Checks a value to see if has a given type
* @function matchType
* @static
* @param {any} value - The value to test
* @param {Function} type - The constructor function that defines the type
* @returns {boolean} Return true if the target is an instance of the given type or if is a primitive value from a given type
* @since 1.2.0
*
* */
function matchType(value, type) {
return value instanceof type || type(value) === value;
}
/**
* Checks a value to see if it belong to a given set of values
* @function hasValue
* @static
* @param {any} target - The value to compare
* @param {array} set - An array consisting of values to compare
* @returns {boolean} Return true if the target is one of the items of the set
* @since 1.2.0
*
* */
function hasValue(target, set) {
return set.indexOf(target) !== -1;
}
/**
* A function to check if value applied to a validation rule is valid or not
* @function checkRule
* @static
* @param {object} target - The object to test for validity
* @param {object} rule - The rule to check
* @param {string|null} rule.prop - If null the rule is applied to the target object otherwise is applied to the specified property
* @param {array} rule.validations - An array of validations to execute
* @param {string} rule.error - The error message to throw in case validation fails
* @param {string} rule.condition - The logical operation to use when checking. It defaults to `and`
* @returns {boolean} Return true if target passes the validation rule, false otherwise
* @since 1.2.0
*
* */
function checkRule(target, rule) {
var i, validation, args;
var source = rule.prop ? target[rule.prop] : target;
var result;
var isAnd = !rule.condition || rule.condition !== 'or';
var isValid = true;
if (source !== undefined) {
isValid = isAnd === true;
for (i = 0; i < rule.validations.length; i++) {
validation = rule.validations[i];
args = [source];
if (validation.args) {
args.push(validation.args);
}
result = validation.check.apply(null, args);
if (isAnd) {
if (!result) {
isValid = false;
break;
}
} else {
if (result) {
isValid = true;
break;
}
}
}
}
fnOpts = ['identifier', 'filename', 'metadata'];
for (i = 0; i < fnOpts.length; i++) {
prop = fnOpts[i];
if (prop in opts && !isFunction(opts[prop])) {
throw new Error('Expected ' + prop + ' configuration to be a function');
return isValid;
}
/**
* Input validation function
* Checks the properties of the configuration object to see if they match their intended type and throw useful error messages
* to help debugging
* @function validateOptions
* @static
* @param {object} opts - The options passed to the constructor
* @since 1.1.0
*
* */
function validateOptions(opts) {
var i, failed, error;
var errorRegexp = /%prop%/;
var rules = [
{
prop: null,
validations: [
{check: isObject},
{check: hasProps, args: ['url', 'gfs']}
],
error: 'Missing required configuration'
},
{
prop: 'gfs',
validations: [
{check: isGfsOrPromise}
],
error: 'Expected gfs configuration to be a Grid instance or a promise'
},
{
prop: 'logLevel',
validations: [
{check: hasValue, args: ['file', 'all']}
],
error: 'Invalid log level configuration. Must be either "file" or "all"'
},
{
prop: 'identifier',
validations: [
{check: isFuncOrGeneratorFunc}
],
error: 'Expected %prop% configuration to be a function or a generator function'
},
{
prop: 'filename',
validations: [
{check: isFuncOrGeneratorFunc}
], error: 'Expected %prop% configuration to be a function or a generator function'
},
{
prop: 'metadata',
validations: [
{check: isFuncOrGeneratorFunc}
],
error: 'Expected %prop% configuration to be a function or a generator function'
},
{
prop: 'chunkSize',
validations: [
{check: isFuncOrGeneratorFunc},
{check: matchType, args: Number}
],
condition: 'or',
error: 'Expected %prop% configuration to be a function, a generator function or a Number'
},
{
prop: 'root',
validations: [
{check: isFuncOrGeneratorFunc},
{check: matchType, args: String}
],
condition: 'or',
error: 'Expected %prop% configuration to be a function, a generator function or a String'
},
{
prop: 'log',
validations: [
{check: isFunction},
{check: matchType, args: Boolean}
],
condition: 'or',
error: 'Expected %prop% configuration to be a function or a Boolean'
}
];
for (i = 0; i < rules.length; i++) {
if (!checkRule(opts, rules[i])) {
failed = rules[i];
break;
}
}
var valueOrFnOpts = [{prop: 'chunkSize', type: Number}, {prop: 'root', type: String}, {prop: 'log', type: Boolean}];
for (i = 0; i < valueOrFnOpts.length; i++) {
prop = valueOrFnOpts[i].prop;
type = valueOrFnOpts[i].type;
if (prop in opts && !isFunction(opts[prop]) && type(opts[prop]) !== opts[prop] && !(opts[prop] instanceof type)) {
throw new Error('Expected ' + prop + ' configuration to be a function or a ' + type.name);
if (failed) {
error = failed.error;
if (errorRegexp.test(failed.error)) {
error = error.replace(errorRegexp, failed.prop);
}
throw new Error(error);
}

@@ -101,5 +427,14 @@ }

isFunction: isFunction,
logMessage: logMessage,
validateOptions: validateOptions
isPromise: isPromise,
isGeneratorFunction: isGeneratorFunction,
isFuncOrGeneratorFunc: isFuncOrGeneratorFunc,
isGenerator: isGenerator,
isGfsOrPromise: isGfsOrPromise,
isObject: isObject,
validateOptions: validateOptions,
hasValue: hasValue,
hasProps: hasProps,
checkRule: checkRule,
matchType: matchType
};
{
"name": "multer-gridfs-storage",
"version": "1.1.1",
"version": "1.2.0",
"description": "Multer storage engine for GridFS",
"main": "index.js",
"scripts": {
"test": "mocha",
"test": "mocha --require babel-register --require babel-polyfill",
"lint": "eslint .",
"cover": "istanbul cover ./node_modules/mocha/bin/_mocha",
"docs": "./node_modules/.bin/jsdoc -c ./conf.json --readme ./README.md",
"coveralls": "npm run cover -- --report lcovonly && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage"

@@ -32,15 +33,20 @@ },

"devDependencies": {
"babel-polyfill": "^6.23.0",
"babel-preset-env": "^1.4.0",
"babel-register": "^6.24.1",
"bluebird": "^3.5.0",
"chai": "^3.5.0",
"chai-interface": "^2.0.3",
"chai-spies": "^0.7.1",
"coveralls": "^2.11.9",
"eslint": "^2.10.2",
"express": "^4.13.4",
"express": "^4.15.2",
"istanbul": "^0.4.3",
"jsdoc": "^3.4.3",
"md5-file": "^2.0.4",
"mocha": "^2.4.5",
"mocha-lcov-reporter": "^1.2.0",
"multer": "^1.1.0",
"multer": "^1.3.0",
"mute": "^2.0.6",
"sinon": "^2.2.0",
"sinon-chai": "^2.10.0",
"supertest": "^1.2.0"

@@ -47,0 +53,0 @@ },

# Multer's GridFS storage engine
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url]
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] ![Npm version][version-image]

@@ -52,2 +52,21 @@ [GridFS](https://docs.mongodb.com/manual/core/gridfs) storage engine for [Multer](https://github.com/expressjs/multer) to store uploaded files directly to MongoDb

Starting from version 1.1.0 the module function can be called with
or without the javascript `new` operator like this
```javascript
var storage = require('multer-gridfs-storage')(options);
var upload = multer({ storage: storage });
//or
var GridFSStorage = require('multer-gridfs-storage');
var storage = new GridFSStorage(options)
var upload = multer({ storage: storage });
```
The 1.2 version brings full support for promises and ES6 generators.
You can check the [wiki][wiki] for more information.
### Options
The options parameter is an object with the following properties.

@@ -57,3 +76,3 @@

Type: **Object** or **Promise**
Type: `object` or `Promise`

@@ -71,3 +90,3 @@ Required if [`url`][url-option] option is not present

var mongo = require('mongodb');
var GridFsStorage = require('multer-gridfs-storage');
var GridFSStorage = require('multer-gridfs-storage');

@@ -80,3 +99,3 @@ var db = new mongo.Db('database', new mongo.Server("127.0.0.1", 27017));

var storage = GridFsStorage({
var storage = GridFSStorage({
gfs: gfs

@@ -90,3 +109,3 @@ });

Type: **String**
Type: `string`

@@ -139,3 +158,3 @@ Required if [`gfs`][gfs-option] option is not present

Type: **Function**
Type: `function` or `function*`

@@ -148,3 +167,3 @@ Not required

By default this module behaves exactly like the default Multer disk storage does.
By default, this module behaves exactly like the default Multer disk storage does.
It generates a 16 bytes long name in hexadecimal format with no extension for the file

@@ -190,3 +209,3 @@ to guarantee that there are very low probabilities of naming collisions. You can override this

Type: **Function**
Type: `function` or `function*`

@@ -221,3 +240,3 @@ Not required

***Important note***
***Note:***

@@ -229,3 +248,3 @@ > Normally you shouldn't use this function

Type: **Function**
Type: `function` or `function*`

@@ -239,3 +258,3 @@ Not required

By default the stored metadata value for uploaded files is `null`.
By default, the stored metadata value for uploaded files is `null`.

@@ -260,9 +279,12 @@ Example:

Type: **Number** or **Function**
Type: `number`, `function` or `function*`
Not required
The preferred size of file chunks. Default value is 261120. You can use a
fixed number as the value or a function to use different values per file.
The preferred size of file chunks in bytes.
Default value is 261120 (255kb).
You can use a fixed number as the value or a function to use different values per upload.
Example using fixed value:

@@ -296,7 +318,7 @@

Type: **String** or **Function**
Type: `string`, `function` or `function*`
Not required
The root collection to store the files. By default this value is `null`.
The root collection to store the files. By default, this value is `null`.
When the value of this property is `null` MongoDb will use the default collection name `'fs'`

@@ -394,3 +416,3 @@ to store files. This value can be changed with this option and you can use a different fixed value

This event is ememitted every time a new file is stored in the db. This is useful when you have
This event is emitted every time a new file is stored in the db. This is useful when you have
a custom logging mechanism and want to record every uploaded file.

@@ -416,3 +438,3 @@

Type: **Boolean** or **Function**
Type: `boolean` or `function`

@@ -425,3 +447,3 @@ Default: `false`

By default the module will not output anything. Set this option to `true` to log when the connection is opened,
By default, the module will not output anything. Set this option to `true` to log when the connection is opened,
files are stored or an error occurs. This is useful when you want to see logging about incoming files.

@@ -457,3 +479,3 @@

Type: **string**
Type: `string`

@@ -509,5 +531,6 @@ Default: `'file'`

[travis-url]: https://travis-ci.org/devconcept/multer-gridfs-storage
[travis-image]: https://travis-ci.org/devconcept/multer-gridfs-storage.svg?branch=master
[travis-image]: https://travis-ci.org/devconcept/multer-gridfs-storage.svg?branch=master "Build status"
[coveralls-url]: https://coveralls.io/github/devconcept/multer-gridfs-storage?branch=master
[coveralls-image]: https://coveralls.io/repos/github/devconcept/multer-gridfs-storage/badge.svg?branch=master
[coveralls-image]: https://coveralls.io/repos/github/devconcept/multer-gridfs-storage/badge.svg?branch=master "Coverage report"
[version-image]:https://img.shields.io/npm/v/multer-gridfs-storage.svg "Npm version"

@@ -523,1 +546,2 @@ [url-option]: #url

[logLevel-option]: #loglevel
[wiki]: https://github.com/devconcept/multer-gridfs-storage/wiki
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