• Jump To … +
    GridFS.js GridStream.js
  • GridFS.js

  • ¶
    /**
     * @fileoverview An easy to use interface for GridFS.
     */
    
    /**
     * _Module dependencies._
     */
    
    var events = require('events');
    
    var GridStore = require('mongodb').GridStore,
        Db = require('mongodb').Db,
        Server = require('mongodb').Server;
    
    /**
     * _Creates an initialized GridFS instance._
     *
     * @class GridFS
     *
     * @param {String} db
     * @param {String} rootcoll (optional)
     * @param {Object} opts (optional)
     *
     * @returns {Object} GridFS instance
     *
     * If a rootcoll is specified, files will be stored in rootcoll.files
     * and rootcoll.chunks. If rootcoll is not already existant, it will be created.
     * If nothing is specified then the default 'fs' will be used.
     *
     * The opts object is passed directly to the underlying mongodb driver. Please
     * see the documentation for mongodb for a list of admissable options.
     *
     * @api public
     */
    
    function GridFS(dbname, filesys, opts){
        var self = this;
    
        var host = process.env['MONGO_NODE_DRIVER_HOST'] || 'localhost';
        var port = process.env['MONGO_NODE_DRIVER_PORT'] || 27017;
    
        opts = opts || { w: 1, journal: false, fsync: false };
    
        this.dbcon = new Db(dbname, new Server(host, port, {}), opts);
    
        this.fs = filesys || GridStore.DEFAULT_ROOT_COLLECTION;
        this.opQueue = [];
        this.emitter = new events.EventEmitter();
        this.busy = false;
    
        this.emitter.on('_op', function(){
            self.busy = false;
            if(self.opQueue.length > 0)
                self.performOp();
        });
    
        if(!(this.dbcon)) throw new Error('Database connection failed.');
    
        this.open();
    };
    
    /**
     * _Performs a queued operation._
     *
     * This is used internally to queue operations.
     *
     * @api private
     */
    
    GridFS.prototype.performOp = function(){
        if(this.dbcon.state === 'connected' && !this.busy){
            var op = this.opQueue.shift(),
                func = op.shift(),
                args = op.pop();
    
            func.apply(this, args);
        }
    }
    
    /**
     * _Stores a file._
     *
     * @param {Buffer} buffer
     * @param {String} filename
     * @param {String} mode
     * @param {Object} options (optional)
     * @param {Function} callback
     *
     * mode can be set to either 'w' to overwrite the content of the file
     * or 'w+' to append to the contents of the file.
     *
     * options can be used to specify the content\_type, metadata and chunk\_size
     *
     * @example var options = { 'content\_type' : 'image/png', 'metadata' : { 'person' : 'George' }, 'chunk\_size' : 1024*4 };
     *
     * The callback takes an error and a result parameter, which provides information
     * about the file after it has been stored.
     *
     * @api public
     */
    
    GridFS.prototype.put = function(buffer, filename, mode, options, callback){
        var args = arguments;
        this.opQueue.push([this._put, args]);
        this.performOp();
    }
    
    /**
     * _Gets a file._
     *
     * @param {String} filename
     * @param {Number} length (optional)
     * @param {Number} offset (optional)
     * @param {Object} options (optional)
     * @param {Function} callback
     *
     * The callback function takes an error and data as parameters. The data object is
     * a buffer.
     *
     * @see put() for option details.
     *
     * @api public
     */
    
    GridFS.prototype.get = function(filename, length, offset, options, callback){
        var args = arguments;
        this.opQueue.push([this._get, args]);
        this.performOp();
    }
    
    /**
     * _Deletes a file._
     *
     * @param {String} filename
     * @param {Function} callback (optional)
     *
     * The callback function takes an error as a parameter.
     *
     * @api public
     */
    
    GridFS.prototype.delete = function(filename, callback){
        var args = arguments;
        this.opQueue.push([this._delete, args]);
        this.performOp();
    }
    
    /**
     * _Opens the database connection._
     *
     * @param {Function} callback (optional)
     *
     * This method should not normally be implemented, unless you have closed the connection
     * and wish to open it again.
     *
     * @example myFS.put(foo, bar, 'w', function(){ myFS.close(); });
     *          ...
     *          myFS.open();
     *
     * By default, a GridFS instance is returned already open().
     * The callback takes an error as it's argument and is called before any queued operations
     * are executed.
     *
     * @api public
     */
    
    GridFS.prototype.open = function(callback){
        var self = this;
    
        this.dbcon.open(function(err){
            if(callback) callback(err);
            else if(err) throw err;
    
            if(self.opQueue.length > 0)
                self.performOp();
        });
    }
    
    /**
     * _Closes the database connection._
     *
     * @param {Function} callback (optional)
     *
     * This should be called once you are done using the GridFS.
     * Functions called after this will be queued to perform when
     * the Grid is reopened. The callback is executed after the
     * closing of the GridFS database connection.
     *
     * @api public
     */
    
    GridFS.prototype.close = function(callback){
        var args = arguments;
        this.opQueue.push([this._close, args]);
        this.performOp();
    }
    
    /**
     * _Stores a file._
     *
     * This is the implementation of put().
     *
     * @api private
     */
    
    GridFS.prototype._put = function(buffer, filename, mode, options, callback){
    
        var self = this;
        var args = Array.prototype.slice.call(arguments, 0);
    
        if(typeof options === 'function'){
            callback = args.pop();
            options = {};
        }
    
        var fs = this.fs;
        var db = this.dbcon;
    
        if(!(buffer instanceof Buffer))
            return callback(new Error('A Buffer object is required.'),null);
    
        options.root = options.root === undefined ? fs : options.root;
    
        var gridStore = new GridStore(db, filename, mode, options);
    
         gridStore.open(function(err, gridStore){
            if(err){
                self.emitter.emit('_op');
                return callback(err, null);
            }
            gridStore.write(buffer, function(err, gridStore){
                if(err){
                    self.emitter.emit('_op');
                    return callback(err, null);
                }
                gridStore.close(function(err, result){
                    self.emitter.emit('_op');
                    callback(err, result);
                });
            });
         });
    };
    
    /**
     * _Gets a file._
     *
     * This is the implementation of get().
     *
     * @api private
     */
    
    GridFS.prototype._get = function(filename, length, offset, options, callback){
    
        var args = Array.prototype.slice.call(arguments, 1);
        var fs = this.fs;
        var db = this.dbcon;
        var self = this;
    
        callback = args.pop();
        length = args.length ? args.shift() : null;
        offset = args.length ? args.shift() : null;
        options = args.length ? args.shift() : null;
    
        GridStore.exist(db, filename, fs, function(err, exists){
            if(err){
                self.emitter.emit('_op');
                return callback(err, null);
            }
    
            if(exists === true){
                new GridStore(db, filename, "r", options).open(function(err, gs){
                    if(offset != null){
                        gs.seek(offset, function(err, gridS){
                            if(err){
                                self.emitter.emit('_op');
                                return callback(err, null);
                            }
                            gridS.read(length, function(err, data){
                                callback(err, data);
                                self.emitter.emit('_op');
                            });
                        });
                    }else{
                        gs.read(length, function(err, data){
                            callback(err, data);
                            self.emitter.emit('_op');
                        });
                    }
                });
            }
            else{
                callback(new Error('The file you wish to read does not exist.'),null);
                self.emitter.emit('_op');
            }
        });
    };
    
    /**
     * _Deletes a file._
     *
     * This is the implementation of delete().
     *
     * @api private
     */
    
    GridFS.prototype._delete = function(filename, callback){
    
        var db = this.dbcon;
        var fs = this.fs;
    
        var self = this;
    
        GridStore.unlink(db, filename, function(err, gs){
            self.emitter.emit('_op');
            if(callback) callback(err);
        });
    };
    
    /**
     * _Closes the database connection._
     *
     * This is the implementation of close().
     *
     * @api private
     */
    
    GridFS.prototype._close = function(callback){
    
        this.dbcon.close();
        this.emitter.emit('_op');
        if(callback) callback();
    }
    
    /**
     * _Exports._
     */
    
    module.exports = GridFS;