93afa50fea171037a9a9e34f030acafe8c005d71
[aai/esr-gui.git] /
1 "use strict";
2
3 /**
4  * @fileOverview GridFS is a tool for MongoDB to store files to the database.
5  * Because of the restrictions of the object size the database can hold, a
6  * facility to split a file into several chunks is needed. The {@link GridStore}
7  * class offers a simplified api to interact with files while managing the
8  * chunks of split files behind the scenes. More information about GridFS can be
9  * found <a href="http://www.mongodb.org/display/DOCS/GridFS">here</a>.
10  *
11  * @example
12  * var MongoClient = require('mongodb').MongoClient,
13  *   GridStore = require('mongodb').GridStore,
14  *   ObjectID = require('mongodb').ObjectID,
15  *   test = require('assert');
16  *
17  * // Connection url
18  * var url = 'mongodb://localhost:27017/test';
19  * // Connect using MongoClient
20  * MongoClient.connect(url, function(err, db) {
21  *   var gridStore = new GridStore(db, null, "w");
22  *   gridStore.open(function(err, gridStore) {
23  *     gridStore.write("hello world!", function(err, gridStore) {
24  *       gridStore.close(function(err, result) {
25  *
26  *         // Let's read the file using object Id
27  *         GridStore.read(db, result._id, function(err, data) {
28  *           test.equal('hello world!', data);
29  *           db.close();
30  *           test.done();
31  *         });
32  *       });
33  *     });
34  *   });
35  * });
36  */
37 var Chunk = require('./chunk'),
38   ObjectID = require('mongodb-core').BSON.ObjectID,
39   ReadPreference = require('../read_preference'),
40   Buffer = require('buffer').Buffer,
41   Collection = require('../collection'),
42   fs = require('fs'),
43   timers = require('timers'),
44   f = require('util').format,
45   util = require('util'),
46   Define = require('../metadata'),
47   MongoError = require('mongodb-core').MongoError,
48   inherits = util.inherits,
49   Duplex = require('stream').Duplex || require('readable-stream').Duplex,
50   shallowClone = require('../utils').shallowClone;
51
52 var REFERENCE_BY_FILENAME = 0,
53   REFERENCE_BY_ID = 1;
54
55 /**
56  * Namespace provided by the mongodb-core and node.js
57  * @external Duplex
58  */
59
60 /**
61  * Create a new GridStore instance
62  *
63  * Modes
64  *  - **"r"** - read only. This is the default mode.
65  *  - **"w"** - write in truncate mode. Existing data will be overwriten.
66  *
67  * @class
68  * @param {Db} db A database instance to interact with.
69  * @param {object} [id] optional unique id for this file
70  * @param {string} [filename] optional filename for this file, no unique constrain on the field
71  * @param {string} mode set the mode for this file.
72  * @param {object} [options=null] Optional settings.
73  * @param {(number|string)} [options.w=null] The write concern.
74  * @param {number} [options.wtimeout=null] The write concern timeout.
75  * @param {boolean} [options.j=false] Specify a journal write concern.
76  * @param {boolean} [options.fsync=false] Specify a file sync write concern.
77  * @param {string} [options.root=null] Root collection to use. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
78  * @param {string} [options.content_type=null] MIME type of the file. Defaults to **{GridStore.DEFAULT_CONTENT_TYPE}**.
79  * @param {number} [options.chunk_size=261120] Size for the chunk. Defaults to **{Chunk.DEFAULT_CHUNK_SIZE}**.
80  * @param {object} [options.metadata=null] Arbitrary data the user wants to store.
81  * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
82  * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
83  * @property {number} chunkSize Get the gridstore chunk size.
84  * @property {number} md5 The md5 checksum for this file.
85  * @property {number} chunkNumber The current chunk number the gridstore has materialized into memory
86  * @return {GridStore} a GridStore instance.
87  * @deprecated Use GridFSBucket API instead
88  */
89 var GridStore = function GridStore(db, id, filename, mode, options) {
90   if(!(this instanceof GridStore)) return new GridStore(db, id, filename, mode, options);
91   var self = this;
92   this.db = db;
93
94   // Handle options
95   if(typeof options === 'undefined') options = {};
96   // Handle mode
97   if(typeof mode === 'undefined') {
98     mode = filename;
99     filename = undefined;
100   } else if(typeof mode == 'object') {
101     options = mode;
102     mode = filename;
103     filename = undefined;
104   }
105
106   if(id instanceof ObjectID) {
107     this.referenceBy = REFERENCE_BY_ID;
108     this.fileId = id;
109     this.filename = filename;
110   } else if(typeof filename == 'undefined') {
111     this.referenceBy = REFERENCE_BY_FILENAME;
112     this.filename = id;
113     if (mode.indexOf('w') != null) {
114       this.fileId = new ObjectID();
115     }
116   } else {
117     this.referenceBy = REFERENCE_BY_ID;
118     this.fileId = id;
119     this.filename = filename;
120   }
121
122   // Set up the rest
123   this.mode = mode == null ? "r" : mode;
124   this.options = options || {};
125
126   // Opened
127   this.isOpen = false;
128
129   // Set the root if overridden
130   this.root = this.options['root'] == null ? GridStore.DEFAULT_ROOT_COLLECTION : this.options['root'];
131   this.position = 0;
132   this.readPreference = this.options.readPreference || db.options.readPreference || ReadPreference.PRIMARY;
133   this.writeConcern = _getWriteConcern(db, this.options);
134   // Set default chunk size
135   this.internalChunkSize = this.options['chunkSize'] == null ? Chunk.DEFAULT_CHUNK_SIZE : this.options['chunkSize'];
136
137   // Get the promiseLibrary
138   var promiseLibrary = this.options.promiseLibrary;
139
140   // No promise library selected fall back
141   if(!promiseLibrary) {
142     promiseLibrary = typeof global.Promise == 'function' ?
143       global.Promise : require('es6-promise').Promise;
144   }
145
146   // Set the promiseLibrary
147   this.promiseLibrary = promiseLibrary;
148
149   Object.defineProperty(this, "chunkSize", { enumerable: true
150    , get: function () {
151        return this.internalChunkSize;
152      }
153    , set: function(value) {
154        if(!(this.mode[0] == "w" && this.position == 0 && this.uploadDate == null)) {
155          this.internalChunkSize = this.internalChunkSize;
156        } else {
157          this.internalChunkSize = value;
158        }
159      }
160   });
161
162   Object.defineProperty(this, "md5", { enumerable: true
163    , get: function () {
164        return this.internalMd5;
165      }
166   });
167
168   Object.defineProperty(this, "chunkNumber", { enumerable: true
169    , get: function () {
170        return this.currentChunk && this.currentChunk.chunkNumber ? this.currentChunk.chunkNumber : null;
171      }
172   });
173 }
174
175 var define = GridStore.define = new Define('Gridstore', GridStore, true);
176
177 /**
178  * The callback format for the Gridstore.open method
179  * @callback GridStore~openCallback
180  * @param {MongoError} error An error instance representing the error during the execution.
181  * @param {GridStore} gridStore The GridStore instance if the open method was successful.
182  */
183
184 /**
185  * Opens the file from the database and initialize this object. Also creates a
186  * new one if file does not exist.
187  *
188  * @method
189  * @param {GridStore~openCallback} [callback] this will be called after executing this method
190  * @return {Promise} returns Promise if no callback passed
191  * @deprecated Use GridFSBucket API instead
192  */
193 GridStore.prototype.open = function(callback) {
194   var self = this;
195   if( this.mode != "w" && this.mode != "w+" && this.mode != "r"){
196     throw MongoError.create({message: "Illegal mode " + this.mode, driver:true});
197   }
198
199   // We provided a callback leg
200   if(typeof callback == 'function') return open(self, callback);
201   // Return promise
202   return new self.promiseLibrary(function(resolve, reject) {
203     open(self, function(err, store) {
204       if(err) return reject(err);
205       resolve(store);
206     })
207   });
208 };
209
210 var open = function(self, callback) {
211   // Get the write concern
212   var writeConcern = _getWriteConcern(self.db, self.options);
213
214   // If we are writing we need to ensure we have the right indexes for md5's
215   if((self.mode == "w" || self.mode == "w+")) {
216     // Get files collection
217     var collection = self.collection();
218     // Put index on filename
219     collection.ensureIndex([['filename', 1]], writeConcern, function(err, index) {
220       // Get chunk collection
221       var chunkCollection = self.chunkCollection();
222       // Make an unique index for compatibility with mongo-cxx-driver:legacy
223       var chunkIndexOptions = shallowClone(writeConcern);
224       chunkIndexOptions.unique = true;
225       // Ensure index on chunk collection
226       chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], chunkIndexOptions, function(err, index) {
227         // Open the connection
228         _open(self, writeConcern, function(err, r) {
229           if(err) return callback(err);
230           self.isOpen = true;
231           callback(err, r);
232         });
233       });
234     });
235   } else {
236     // Open the gridstore
237     _open(self, writeConcern, function(err, r) {
238       if(err) return callback(err);
239       self.isOpen = true;
240       callback(err, r);
241     });
242   }
243 }
244
245 // Push the definition for open
246 define.classMethod('open', {callback: true, promise:true});
247
248 /**
249  * Verify if the file is at EOF.
250  *
251  * @method
252  * @return {boolean} true if the read/write head is at the end of this file.
253  * @deprecated Use GridFSBucket API instead
254  */
255 GridStore.prototype.eof = function() {
256   return this.position == this.length ? true : false;
257 }
258
259 define.classMethod('eof', {callback: false, promise:false, returns: [Boolean]});
260
261 /**
262  * The callback result format.
263  * @callback GridStore~resultCallback
264  * @param {MongoError} error An error instance representing the error during the execution.
265  * @param {object} result The result from the callback.
266  */
267
268 /**
269  * Retrieves a single character from this file.
270  *
271  * @method
272  * @param {GridStore~resultCallback} [callback] this gets called after this method is executed. Passes null to the first parameter and the character read to the second or null to the second if the read/write head is at the end of the file.
273  * @return {Promise} returns Promise if no callback passed
274  * @deprecated Use GridFSBucket API instead
275  */
276 GridStore.prototype.getc = function(callback) {
277   var self = this;
278   // We provided a callback leg
279   if(typeof callback == 'function') return eof(self, callback);
280   // Return promise
281   return new self.promiseLibrary(function(resolve, reject) {
282     eof(self, function(err, r) {
283       if(err) return reject(err);
284       resolve(r);
285     })
286   });
287 }
288
289 var eof = function(self, callback) {
290   if(self.eof()) {
291     callback(null, null);
292   } else if(self.currentChunk.eof()) {
293     nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) {
294       self.currentChunk = chunk;
295       self.position = self.position + 1;
296       callback(err, self.currentChunk.getc());
297     });
298   } else {
299     self.position = self.position + 1;
300     callback(null, self.currentChunk.getc());
301   }
302 }
303
304 define.classMethod('getc', {callback: true, promise:true});
305
306 /**
307  * Writes a string to the file with a newline character appended at the end if
308  * the given string does not have one.
309  *
310  * @method
311  * @param {string} string the string to write.
312  * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
313  * @return {Promise} returns Promise if no callback passed
314  * @deprecated Use GridFSBucket API instead
315  */
316 GridStore.prototype.puts = function(string, callback) {
317   var self = this;
318   var finalString = string.match(/\n$/) == null ? string + "\n" : string;
319   // We provided a callback leg
320   if(typeof callback == 'function') return this.write(finalString, callback);
321   // Return promise
322   return new self.promiseLibrary(function(resolve, reject) {
323     self.write(finalString, function(err, r) {
324       if(err) return reject(err);
325       resolve(r);
326     })
327   });
328 }
329
330 define.classMethod('puts', {callback: true, promise:true});
331
332 /**
333  * Return a modified Readable stream including a possible transform method.
334  *
335  * @method
336  * @return {GridStoreStream}
337  * @deprecated Use GridFSBucket API instead
338  */
339 GridStore.prototype.stream = function() {
340   return new GridStoreStream(this);
341 }
342
343 define.classMethod('stream', {callback: false, promise:false, returns: [GridStoreStream]});
344
345 /**
346  * Writes some data. This method will work properly only if initialized with mode "w" or "w+".
347  *
348  * @method
349  * @param {(string|Buffer)} data the data to write.
350  * @param {boolean} [close] closes this file after writing if set to true.
351  * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
352  * @return {Promise} returns Promise if no callback passed
353  * @deprecated Use GridFSBucket API instead
354  */
355 GridStore.prototype.write = function write(data, close, callback) {
356   var self = this;
357   // We provided a callback leg
358   if(typeof callback == 'function') return _writeNormal(this, data, close, callback);
359   // Return promise
360   return new self.promiseLibrary(function(resolve, reject) {
361     _writeNormal(self, data, close, function(err, r) {
362       if(err) return reject(err);
363       resolve(r);
364     })
365   });
366 }
367
368 define.classMethod('write', {callback: true, promise:true});
369
370 /**
371  * Handles the destroy part of a stream
372  *
373  * @method
374  * @result {null}
375  * @deprecated Use GridFSBucket API instead
376  */
377 GridStore.prototype.destroy = function destroy() {
378   // close and do not emit any more events. queued data is not sent.
379   if(!this.writable) return;
380   this.readable = false;
381   if(this.writable) {
382     this.writable = false;
383     this._q.length = 0;
384     this.emit('close');
385   }
386 }
387
388 define.classMethod('destroy', {callback: false, promise:false});
389
390 /**
391  * Stores a file from the file system to the GridFS database.
392  *
393  * @method
394  * @param {(string|Buffer|FileHandle)} file the file to store.
395  * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
396  * @return {Promise} returns Promise if no callback passed
397  * @deprecated Use GridFSBucket API instead
398  */
399 GridStore.prototype.writeFile = function (file, callback) {
400   var self = this;
401   // We provided a callback leg
402   if(typeof callback == 'function') return writeFile(self, file, callback);
403   // Return promise
404   return new self.promiseLibrary(function(resolve, reject) {
405     writeFile(self, file, function(err, r) {
406       if(err) return reject(err);
407       resolve(r);
408     })
409   });
410 };
411
412 var writeFile = function(self, file, callback) {
413   if (typeof file === 'string') {
414     fs.open(file, 'r', function (err, fd) {
415       if(err) return callback(err);
416       self.writeFile(fd, callback);
417     });
418     return;
419   }
420
421   self.open(function (err, self) {
422     if(err) return callback(err, self);
423
424     fs.fstat(file, function (err, stats) {
425       if(err) return callback(err, self);
426
427       var offset = 0;
428       var index = 0;
429       var numberOfChunksLeft = Math.min(stats.size / self.chunkSize);
430
431       // Write a chunk
432       var writeChunk = function() {
433         fs.read(file, self.chunkSize, offset, 'binary', function(err, data, bytesRead) {
434           if(err) return callback(err, self);
435
436           offset = offset + bytesRead;
437
438           // Create a new chunk for the data
439           var chunk = new Chunk(self, {n:index++}, self.writeConcern);
440           chunk.write(data, function(err, chunk) {
441             if(err) return callback(err, self);
442
443             chunk.save({}, function(err, result) {
444               if(err) return callback(err, self);
445
446               self.position = self.position + data.length;
447
448               // Point to current chunk
449               self.currentChunk = chunk;
450
451               if(offset >= stats.size) {
452                 fs.close(file);
453                 self.close(function(err, result) {
454                   if(err) return callback(err, self);
455                   return callback(null, self);
456                 });
457               } else {
458                 return process.nextTick(writeChunk);
459               }
460             });
461           });
462         });
463       }
464
465       // Process the first write
466       process.nextTick(writeChunk);
467     });
468   });
469 }
470
471 define.classMethod('writeFile', {callback: true, promise:true});
472
473 /**
474  * Saves this file to the database. This will overwrite the old entry if it
475  * already exists. This will work properly only if mode was initialized to
476  * "w" or "w+".
477  *
478  * @method
479  * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
480  * @return {Promise} returns Promise if no callback passed
481  * @deprecated Use GridFSBucket API instead
482  */
483 GridStore.prototype.close = function(callback) {
484   var self = this;
485   // We provided a callback leg
486   if(typeof callback == 'function') return close(self, callback);
487   // Return promise
488   return new self.promiseLibrary(function(resolve, reject) {
489     close(self, function(err, r) {
490       if(err) return reject(err);
491       resolve(r);
492     })
493   });
494 };
495
496 var close = function(self, callback) {
497   if(self.mode[0] == "w") {
498     // Set up options
499     var options = self.writeConcern;
500
501     if(self.currentChunk != null && self.currentChunk.position > 0) {
502       self.currentChunk.save({}, function(err, chunk) {
503         if(err && typeof callback == 'function') return callback(err);
504
505         self.collection(function(err, files) {
506           if(err && typeof callback == 'function') return callback(err);
507
508           // Build the mongo object
509           if(self.uploadDate != null) {
510             buildMongoObject(self, function(err, mongoObject) {
511               if(err) {
512                 if(typeof callback == 'function') return callback(err); else throw err;
513               }
514
515               files.save(mongoObject, options, function(err) {
516                 if(typeof callback == 'function')
517                   callback(err, mongoObject);
518               });
519             });
520           } else {
521             self.uploadDate = new Date();
522             buildMongoObject(self, function(err, mongoObject) {
523               if(err) {
524                 if(typeof callback == 'function') return callback(err); else throw err;
525               }
526
527               files.save(mongoObject, options, function(err) {
528                 if(typeof callback == 'function')
529                   callback(err, mongoObject);
530               });
531             });
532           }
533         });
534       });
535     } else {
536       self.collection(function(err, files) {
537         if(err && typeof callback == 'function') return callback(err);
538
539         self.uploadDate = new Date();
540         buildMongoObject(self, function(err, mongoObject) {
541           if(err) {
542             if(typeof callback == 'function') return callback(err); else throw err;
543           }
544
545           files.save(mongoObject, options, function(err) {
546             if(typeof callback == 'function')
547               callback(err, mongoObject);
548           });
549         });
550       });
551     }
552   } else if(self.mode[0] == "r") {
553     if(typeof callback == 'function')
554       callback(null, null);
555   } else {
556     if(typeof callback == 'function')
557       callback(MongoError.create({message: f("Illegal mode %s", self.mode), driver:true}));
558   }
559 }
560
561 define.classMethod('close', {callback: true, promise:true});
562
563 /**
564  * The collection callback format.
565  * @callback GridStore~collectionCallback
566  * @param {MongoError} error An error instance representing the error during the execution.
567  * @param {Collection} collection The collection from the command execution.
568  */
569
570 /**
571  * Retrieve this file's chunks collection.
572  *
573  * @method
574  * @param {GridStore~collectionCallback} callback the command callback.
575  * @return {Collection}
576  * @deprecated Use GridFSBucket API instead
577  */
578 GridStore.prototype.chunkCollection = function(callback) {
579   if(typeof callback == 'function')
580     return this.db.collection((this.root + ".chunks"), callback);
581   return this.db.collection((this.root + ".chunks"));
582 };
583
584 define.classMethod('chunkCollection', {callback: true, promise:false, returns: [Collection]});
585
586 /**
587  * Deletes all the chunks of this file in the database.
588  *
589  * @method
590  * @param {GridStore~resultCallback} [callback] the command callback.
591  * @return {Promise} returns Promise if no callback passed
592  * @deprecated Use GridFSBucket API instead
593  */
594 GridStore.prototype.unlink = function(callback) {
595   var self = this;
596   // We provided a callback leg
597   if(typeof callback == 'function') return unlink(self, callback);
598   // Return promise
599   return new self.promiseLibrary(function(resolve, reject) {
600     unlink(self, function(err, r) {
601       if(err) return reject(err);
602       resolve(r);
603     })
604   });
605 };
606
607 var unlink = function(self, callback) {
608   deleteChunks(self, function(err) {
609     if(err!==null) {
610       err.message = "at deleteChunks: " + err.message;
611       return callback(err);
612     }
613
614     self.collection(function(err, collection) {
615       if(err!==null) {
616         err.message = "at collection: " + err.message;
617         return callback(err);
618       }
619
620       collection.remove({'_id':self.fileId}, self.writeConcern, function(err) {
621         callback(err, self);
622       });
623     });
624   });
625 }
626
627 define.classMethod('unlink', {callback: true, promise:true});
628
629 /**
630  * Retrieves the file collection associated with this object.
631  *
632  * @method
633  * @param {GridStore~collectionCallback} callback the command callback.
634  * @return {Collection}
635  * @deprecated Use GridFSBucket API instead
636  */
637 GridStore.prototype.collection = function(callback) {
638   if(typeof callback == 'function')
639     this.db.collection(this.root + ".files", callback);
640   return this.db.collection(this.root + ".files");
641 };
642
643 define.classMethod('collection', {callback: true, promise:false, returns: [Collection]});
644
645 /**
646  * The readlines callback format.
647  * @callback GridStore~readlinesCallback
648  * @param {MongoError} error An error instance representing the error during the execution.
649  * @param {string[]} strings The array of strings returned.
650  */
651
652 /**
653  * Read the entire file as a list of strings splitting by the provided separator.
654  *
655  * @method
656  * @param {string} [separator] The character to be recognized as the newline separator.
657  * @param {GridStore~readlinesCallback} [callback] the command callback.
658  * @return {Promise} returns Promise if no callback passed
659  * @deprecated Use GridFSBucket API instead
660  */
661 GridStore.prototype.readlines = function(separator, callback) {
662   var self = this;
663   var args = Array.prototype.slice.call(arguments, 0);
664   callback = args.pop();
665   if(typeof callback != 'function') args.push(callback);
666   separator = args.length ? args.shift() : "\n";
667   separator = separator || "\n";
668
669   // We provided a callback leg
670   if(typeof callback == 'function') return readlines(self, separator, callback);
671
672   // Return promise
673   return new self.promiseLibrary(function(resolve, reject) {
674     readlines(self, separator, function(err, r) {
675       if(err) return reject(err);
676       resolve(r);
677     })
678   });
679 };
680
681 var readlines = function(self, separator, callback) {
682   self.read(function(err, data) {
683     if(err) return callback(err);
684
685     var items = data.toString().split(separator);
686     items = items.length > 0 ? items.splice(0, items.length - 1) : [];
687     for(var i = 0; i < items.length; i++) {
688       items[i] = items[i] + separator;
689     }
690
691     callback(null, items);
692   });
693 }
694
695 define.classMethod('readlines', {callback: true, promise:true});
696
697 /**
698  * Deletes all the chunks of this file in the database if mode was set to "w" or
699  * "w+" and resets the read/write head to the initial position.
700  *
701  * @method
702  * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
703  * @return {Promise} returns Promise if no callback passed
704  * @deprecated Use GridFSBucket API instead
705  */
706 GridStore.prototype.rewind = function(callback) {
707   var self = this;
708   // We provided a callback leg
709   if(typeof callback == 'function') return rewind(self, callback);
710   // Return promise
711   return new self.promiseLibrary(function(resolve, reject) {
712     rewind(self, function(err, r) {
713       if(err) return reject(err);
714       resolve(r);
715     })
716   });
717 };
718
719 var rewind = function(self, callback) {
720   if(self.currentChunk.chunkNumber != 0) {
721     if(self.mode[0] == "w") {
722       deleteChunks(self, function(err, gridStore) {
723         if(err) return callback(err);
724         self.currentChunk = new Chunk(self, {'n': 0}, self.writeConcern);
725         self.position = 0;
726         callback(null, self);
727       });
728     } else {
729       self.currentChunk(0, function(err, chunk) {
730         if(err) return callback(err);
731         self.currentChunk = chunk;
732         self.currentChunk.rewind();
733         self.position = 0;
734         callback(null, self);
735       });
736     }
737   } else {
738     self.currentChunk.rewind();
739     self.position = 0;
740     callback(null, self);
741   }
742 }
743
744 define.classMethod('rewind', {callback: true, promise:true});
745
746 /**
747  * The read callback format.
748  * @callback GridStore~readCallback
749  * @param {MongoError} error An error instance representing the error during the execution.
750  * @param {Buffer} data The data read from the GridStore object
751  */
752
753 /**
754  * Retrieves the contents of this file and advances the read/write head. Works with Buffers only.
755  *
756  * There are 3 signatures for this method:
757  *
758  * (callback)
759  * (length, callback)
760  * (length, buffer, callback)
761  *
762  * @method
763  * @param {number} [length] the number of characters to read. Reads all the characters from the read/write head to the EOF if not specified.
764  * @param {(string|Buffer)} [buffer] a string to hold temporary data. This is used for storing the string data read so far when recursively calling this method.
765  * @param {GridStore~readCallback} [callback] the command callback.
766  * @return {Promise} returns Promise if no callback passed
767  * @deprecated Use GridFSBucket API instead
768  */
769 GridStore.prototype.read = function(length, buffer, callback) {
770   var self = this;
771
772   var args = Array.prototype.slice.call(arguments, 0);
773   callback = args.pop();
774   if(typeof callback != 'function') args.push(callback);
775   length = args.length ? args.shift() : null;
776   buffer = args.length ? args.shift() : null;
777   // We provided a callback leg
778   if(typeof callback == 'function') return read(self, length, buffer, callback);
779   // Return promise
780   return new self.promiseLibrary(function(resolve, reject) {
781     read(self, length, buffer, function(err, r) {
782       if(err) return reject(err);
783       resolve(r);
784     })
785   });
786 }
787
788 var read = function(self, length, buffer, callback) {
789   // The data is a c-terminated string and thus the length - 1
790   var finalLength = length == null ? self.length - self.position : length;
791   var finalBuffer = buffer == null ? new Buffer(finalLength) : buffer;
792   // Add a index to buffer to keep track of writing position or apply current index
793   finalBuffer._index = buffer != null && buffer._index != null ? buffer._index : 0;
794
795   if((self.currentChunk.length() - self.currentChunk.position + finalBuffer._index) >= finalLength) {
796     var slice = self.currentChunk.readSlice(finalLength - finalBuffer._index);
797     // Copy content to final buffer
798     slice.copy(finalBuffer, finalBuffer._index);
799     // Update internal position
800     self.position = self.position + finalBuffer.length;
801     // Check if we don't have a file at all
802     if(finalLength == 0 && finalBuffer.length == 0) return callback(MongoError.create({message: "File does not exist", driver:true}), null);
803     // Else return data
804     return callback(null, finalBuffer);
805   }
806
807   // Read the next chunk
808   var slice = self.currentChunk.readSlice(self.currentChunk.length() - self.currentChunk.position);
809   // Copy content to final buffer
810   slice.copy(finalBuffer, finalBuffer._index);
811   // Update index position
812   finalBuffer._index += slice.length;
813
814   // Load next chunk and read more
815   nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) {
816     if(err) return callback(err);
817
818     if(chunk.length() > 0) {
819       self.currentChunk = chunk;
820       self.read(length, finalBuffer, callback);
821     } else {
822       if(finalBuffer._index > 0) {
823         callback(null, finalBuffer)
824       } else {
825         callback(MongoError.create({message: "no chunks found for file, possibly corrupt", driver:true}), null);
826       }
827     }
828   });
829 }
830
831 define.classMethod('read', {callback: true, promise:true});
832
833 /**
834  * The tell callback format.
835  * @callback GridStore~tellCallback
836  * @param {MongoError} error An error instance representing the error during the execution.
837  * @param {number} position The current read position in the GridStore.
838  */
839
840 /**
841  * Retrieves the position of the read/write head of this file.
842  *
843  * @method
844  * @param {number} [length] the number of characters to read. Reads all the characters from the read/write head to the EOF if not specified.
845  * @param {(string|Buffer)} [buffer] a string to hold temporary data. This is used for storing the string data read so far when recursively calling this method.
846  * @param {GridStore~tellCallback} [callback] the command callback.
847  * @return {Promise} returns Promise if no callback passed
848  * @deprecated Use GridFSBucket API instead
849  */
850 GridStore.prototype.tell = function(callback) {
851   var self = this;
852   // We provided a callback leg
853   if(typeof callback == 'function') return callback(null, this.position);
854   // Return promise
855   return new self.promiseLibrary(function(resolve, reject) {
856     resolve(self.position);
857   });
858 };
859
860 define.classMethod('tell', {callback: true, promise:true});
861
862 /**
863  * The tell callback format.
864  * @callback GridStore~gridStoreCallback
865  * @param {MongoError} error An error instance representing the error during the execution.
866  * @param {GridStore} gridStore The gridStore.
867  */
868
869 /**
870  * Moves the read/write head to a new location.
871  *
872  * There are 3 signatures for this method
873  *
874  * Seek Location Modes
875  *  - **GridStore.IO_SEEK_SET**, **(default)** set the position from the start of the file.
876  *  - **GridStore.IO_SEEK_CUR**, set the position from the current position in the file.
877  *  - **GridStore.IO_SEEK_END**, set the position from the end of the file.
878  *
879  * @method
880  * @param {number} [position] the position to seek to
881  * @param {number} [seekLocation] seek mode. Use one of the Seek Location modes.
882  * @param {GridStore~gridStoreCallback} [callback] the command callback.
883  * @return {Promise} returns Promise if no callback passed
884  * @deprecated Use GridFSBucket API instead
885  */
886 GridStore.prototype.seek = function(position, seekLocation, callback) {
887   var self = this;
888
889   var args = Array.prototype.slice.call(arguments, 1);
890   callback = args.pop();
891   if(typeof callback != 'function') args.push(callback);
892   seekLocation = args.length ? args.shift() : null;
893
894   // We provided a callback leg
895   if(typeof callback == 'function') return seek(self, position, seekLocation, callback);
896   // Return promise
897   return new self.promiseLibrary(function(resolve, reject) {
898     seek(self, position, seekLocation, function(err, r) {
899       if(err) return reject(err);
900       resolve(r);
901     })
902   });
903 }
904
905 var seek = function(self, position, seekLocation, callback) {
906   // Seek only supports read mode
907   if(self.mode != 'r') {
908     return callback(MongoError.create({message: "seek is only supported for mode r", driver:true}))
909   }
910
911   var seekLocationFinal = seekLocation == null ? GridStore.IO_SEEK_SET : seekLocation;
912   var finalPosition = position;
913   var targetPosition = 0;
914
915   // Calculate the position
916   if(seekLocationFinal == GridStore.IO_SEEK_CUR) {
917     targetPosition = self.position + finalPosition;
918   } else if(seekLocationFinal == GridStore.IO_SEEK_END) {
919     targetPosition = self.length + finalPosition;
920   } else {
921     targetPosition = finalPosition;
922   }
923
924   // Get the chunk
925   var newChunkNumber = Math.floor(targetPosition/self.chunkSize);
926   var seekChunk = function() {
927     nthChunk(self, newChunkNumber, function(err, chunk) {
928       if(err) return callback(err, null);
929       if(chunk == null) return callback(new Error('no chunk found'));
930
931       // Set the current chunk
932       self.currentChunk = chunk;
933       self.position = targetPosition;
934       self.currentChunk.position = (self.position % self.chunkSize);
935       callback(err, self);
936     });
937   };
938
939   seekChunk();
940 }
941
942 define.classMethod('seek', {callback: true, promise:true});
943
944 /**
945  * @ignore
946  */
947 var _open = function(self, options, callback) {
948   var collection = self.collection();
949   // Create the query
950   var query = self.referenceBy == REFERENCE_BY_ID ? {_id:self.fileId} : {filename:self.filename};
951   query = null == self.fileId && self.filename == null ? null : query;
952   options.readPreference = self.readPreference;
953
954   // Fetch the chunks
955   if(query != null) {
956     collection.findOne(query, options, function(err, doc) {
957       if(err) return error(err);
958
959       // Check if the collection for the files exists otherwise prepare the new one
960       if(doc != null) {
961         self.fileId = doc._id;
962         // Prefer a new filename over the existing one if this is a write
963         self.filename = ((self.mode == 'r') || (self.filename == undefined)) ? doc.filename : self.filename;
964         self.contentType = doc.contentType;
965         self.internalChunkSize = doc.chunkSize;
966         self.uploadDate = doc.uploadDate;
967         self.aliases = doc.aliases;
968         self.length = doc.length;
969         self.metadata = doc.metadata;
970         self.internalMd5 = doc.md5;
971       } else if (self.mode != 'r') {
972         self.fileId = self.fileId == null ? new ObjectID() : self.fileId;
973         self.contentType = GridStore.DEFAULT_CONTENT_TYPE;
974         self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize;
975         self.length = 0;
976       } else {
977         self.length = 0;
978         var txtId = self.fileId instanceof ObjectID ? self.fileId.toHexString() : self.fileId;
979         return error(MongoError.create({message: f("file with id %s not opened for writing", (self.referenceBy == REFERENCE_BY_ID ? txtId : self.filename)), driver:true}), self);
980       }
981
982       // Process the mode of the object
983       if(self.mode == "r") {
984         nthChunk(self, 0, options, function(err, chunk) {
985           if(err) return error(err);
986           self.currentChunk = chunk;
987           self.position = 0;
988           callback(null, self);
989         });
990       } else if(self.mode == "w" && doc) {
991         // Delete any existing chunks
992         deleteChunks(self, options, function(err, result) {
993           if(err) return error(err);
994           self.currentChunk = new Chunk(self, {'n':0}, self.writeConcern);
995           self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type'];
996           self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size'];
997           self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
998           self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
999           self.position = 0;
1000           callback(null, self);
1001         });
1002       } else if(self.mode == "w") {
1003         self.currentChunk = new Chunk(self, {'n':0}, self.writeConcern);
1004         self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type'];
1005         self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size'];
1006         self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
1007         self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
1008         self.position = 0;
1009         callback(null, self);
1010       } else if(self.mode == "w+") {
1011         nthChunk(self, lastChunkNumber(self), options, function(err, chunk) {
1012           if(err) return error(err);
1013           // Set the current chunk
1014           self.currentChunk = chunk == null ? new Chunk(self, {'n':0}, self.writeConcern) : chunk;
1015           self.currentChunk.position = self.currentChunk.data.length();
1016           self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
1017           self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
1018           self.position = self.length;
1019           callback(null, self);
1020         });
1021       }
1022     });
1023   } else {
1024     // Write only mode
1025     self.fileId = null == self.fileId ? new ObjectID() : self.fileId;
1026     self.contentType = GridStore.DEFAULT_CONTENT_TYPE;
1027     self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize;
1028     self.length = 0;
1029
1030     var collection2 = self.chunkCollection();
1031     // No file exists set up write mode
1032     if(self.mode == "w") {
1033       // Delete any existing chunks
1034       deleteChunks(self, options, function(err, result) {
1035         if(err) return error(err);
1036         self.currentChunk = new Chunk(self, {'n':0}, self.writeConcern);
1037         self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type'];
1038         self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size'];
1039         self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
1040         self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
1041         self.position = 0;
1042         callback(null, self);
1043       });
1044     } else if(self.mode == "w+") {
1045       nthChunk(self, lastChunkNumber(self), options, function(err, chunk) {
1046         if(err) return error(err);
1047         // Set the current chunk
1048         self.currentChunk = chunk == null ? new Chunk(self, {'n':0}, self.writeConcern) : chunk;
1049         self.currentChunk.position = self.currentChunk.data.length();
1050         self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
1051         self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
1052         self.position = self.length;
1053         callback(null, self);
1054       });
1055     }
1056   }
1057
1058   // only pass error to callback once
1059   function error (err) {
1060     if(error.err) return;
1061     callback(error.err = err);
1062   }
1063 };
1064
1065 /**
1066  * @ignore
1067  */
1068 var writeBuffer = function(self, buffer, close, callback) {
1069   if(typeof close === "function") { callback = close; close = null; }
1070   var finalClose = typeof close == 'boolean' ? close : false;
1071
1072   if(self.mode != "w") {
1073     callback(MongoError.create({message: f("file with id %s not opened for writing", (self.referenceBy == REFERENCE_BY_ID ? self.referenceBy : self.filename)), driver:true}), null);
1074   } else {
1075     if(self.currentChunk.position + buffer.length >= self.chunkSize) {
1076       // Write out the current Chunk and then keep writing until we have less data left than a chunkSize left
1077       // to a new chunk (recursively)
1078       var previousChunkNumber = self.currentChunk.chunkNumber;
1079       var leftOverDataSize = self.chunkSize - self.currentChunk.position;
1080       var firstChunkData = buffer.slice(0, leftOverDataSize);
1081       var leftOverData = buffer.slice(leftOverDataSize);
1082       // A list of chunks to write out
1083       var chunksToWrite = [self.currentChunk.write(firstChunkData)];
1084       // If we have more data left than the chunk size let's keep writing new chunks
1085       while(leftOverData.length >= self.chunkSize) {
1086         // Create a new chunk and write to it
1087         var newChunk = new Chunk(self, {'n': (previousChunkNumber + 1)}, self.writeConcern);
1088         var firstChunkData = leftOverData.slice(0, self.chunkSize);
1089         leftOverData = leftOverData.slice(self.chunkSize);
1090         // Update chunk number
1091         previousChunkNumber = previousChunkNumber + 1;
1092         // Write data
1093         newChunk.write(firstChunkData);
1094         // Push chunk to save list
1095         chunksToWrite.push(newChunk);
1096       }
1097
1098       // Set current chunk with remaining data
1099       self.currentChunk = new Chunk(self, {'n': (previousChunkNumber + 1)}, self.writeConcern);
1100       // If we have left over data write it
1101       if(leftOverData.length > 0) self.currentChunk.write(leftOverData);
1102
1103       // Update the position for the gridstore
1104       self.position = self.position + buffer.length;
1105       // Total number of chunks to write
1106       var numberOfChunksToWrite = chunksToWrite.length;
1107
1108       for(var i = 0; i < chunksToWrite.length; i++) {
1109         chunksToWrite[i].save({}, function(err, result) {
1110           if(err) return callback(err);
1111
1112           numberOfChunksToWrite = numberOfChunksToWrite - 1;
1113
1114           if(numberOfChunksToWrite <= 0) {
1115             // We care closing the file before returning
1116             if(finalClose) {
1117               return self.close(function(err, result) {
1118                 callback(err, self);
1119               });
1120             }
1121
1122             // Return normally
1123             return callback(null, self);
1124           }
1125         });
1126       }
1127     } else {
1128       // Update the position for the gridstore
1129       self.position = self.position + buffer.length;
1130       // We have less data than the chunk size just write it and callback
1131       self.currentChunk.write(buffer);
1132       // We care closing the file before returning
1133       if(finalClose) {
1134         return self.close(function(err, result) {
1135           callback(err, self);
1136         });
1137       }
1138       // Return normally
1139       return callback(null, self);
1140     }
1141   }
1142 };
1143
1144 /**
1145  * Creates a mongoDB object representation of this object.
1146  *
1147  *        <pre><code>
1148  *        {
1149  *          '_id' : , // {number} id for this file
1150  *          'filename' : , // {string} name for this file
1151  *          'contentType' : , // {string} mime type for this file
1152  *          'length' : , // {number} size of this file?
1153  *          'chunksize' : , // {number} chunk size used by this file
1154  *          'uploadDate' : , // {Date}
1155  *          'aliases' : , // {array of string}
1156  *          'metadata' : , // {string}
1157  *        }
1158  *        </code></pre>
1159  *
1160  * @ignore
1161  */
1162 var buildMongoObject = function(self, callback) {
1163   // Calcuate the length
1164   var mongoObject = {
1165     '_id': self.fileId,
1166     'filename': self.filename,
1167     'contentType': self.contentType,
1168     'length': self.position ? self.position : 0,
1169     'chunkSize': self.chunkSize,
1170     'uploadDate': self.uploadDate,
1171     'aliases': self.aliases,
1172     'metadata': self.metadata
1173   };
1174
1175   var md5Command = {filemd5:self.fileId, root:self.root};
1176   self.db.command(md5Command, function(err, results) {
1177     if(err) return callback(err);
1178
1179     mongoObject.md5 = results.md5;
1180     callback(null, mongoObject);
1181   });
1182 };
1183
1184 /**
1185  * Gets the nth chunk of this file.
1186  * @ignore
1187  */
1188 var nthChunk = function(self, chunkNumber, options, callback) {
1189   if(typeof options == 'function') {
1190     callback = options;
1191     options = {};
1192   }
1193
1194   options = options || self.writeConcern;
1195   options.readPreference = self.readPreference;
1196   // Get the nth chunk
1197   self.chunkCollection().findOne({'files_id':self.fileId, 'n':chunkNumber}, options, function(err, chunk) {
1198     if(err) return callback(err);
1199
1200     var finalChunk = chunk == null ? {} : chunk;
1201     callback(null, new Chunk(self, finalChunk, self.writeConcern));
1202   });
1203 };
1204
1205 /**
1206  * @ignore
1207  */
1208 var lastChunkNumber = function(self) {
1209   return Math.floor((self.length ? self.length - 1 : 0)/self.chunkSize);
1210 };
1211
1212 /**
1213  * Deletes all the chunks of this file in the database.
1214  *
1215  * @ignore
1216  */
1217 var deleteChunks = function(self, options, callback) {
1218   if(typeof options == 'function') {
1219     callback = options;
1220     options = {};
1221   }
1222
1223   options = options || self.writeConcern;
1224
1225   if(self.fileId != null) {
1226     self.chunkCollection().remove({'files_id':self.fileId}, options, function(err, result) {
1227       if(err) return callback(err, false);
1228       callback(null, true);
1229     });
1230   } else {
1231     callback(null, true);
1232   }
1233 };
1234
1235 /**
1236 * The collection to be used for holding the files and chunks collection.
1237 *
1238 * @classconstant DEFAULT_ROOT_COLLECTION
1239 **/
1240 GridStore.DEFAULT_ROOT_COLLECTION = 'fs';
1241
1242 /**
1243 * Default file mime type
1244 *
1245 * @classconstant DEFAULT_CONTENT_TYPE
1246 **/
1247 GridStore.DEFAULT_CONTENT_TYPE = 'binary/octet-stream';
1248
1249 /**
1250 * Seek mode where the given length is absolute.
1251 *
1252 * @classconstant IO_SEEK_SET
1253 **/
1254 GridStore.IO_SEEK_SET = 0;
1255
1256 /**
1257 * Seek mode where the given length is an offset to the current read/write head.
1258 *
1259 * @classconstant IO_SEEK_CUR
1260 **/
1261 GridStore.IO_SEEK_CUR = 1;
1262
1263 /**
1264 * Seek mode where the given length is an offset to the end of the file.
1265 *
1266 * @classconstant IO_SEEK_END
1267 **/
1268 GridStore.IO_SEEK_END = 2;
1269
1270 /**
1271  * Checks if a file exists in the database.
1272  *
1273  * @method
1274  * @static
1275  * @param {Db} db the database to query.
1276  * @param {string} name The name of the file to look for.
1277  * @param {string} [rootCollection] The root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
1278  * @param {object} [options=null] Optional settings.
1279  * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
1280  * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
1281  * @param {GridStore~resultCallback} [callback] result from exists.
1282  * @return {Promise} returns Promise if no callback passed
1283  * @deprecated Use GridFSBucket API instead
1284  */
1285 GridStore.exist = function(db, fileIdObject, rootCollection, options, callback) {
1286   var args = Array.prototype.slice.call(arguments, 2);
1287   callback = args.pop();
1288   if(typeof callback != 'function') args.push(callback);
1289   rootCollection = args.length ? args.shift() : null;
1290   options = args.length ? args.shift() : {};
1291   options = options || {};
1292
1293   // Get the promiseLibrary
1294   var promiseLibrary = options.promiseLibrary;
1295
1296   // No promise library selected fall back
1297   if(!promiseLibrary) {
1298     promiseLibrary = typeof global.Promise == 'function' ?
1299       global.Promise : require('es6-promise').Promise;
1300   }
1301
1302   // We provided a callback leg
1303   if(typeof callback == 'function') return exists(db, fileIdObject, rootCollection, options, callback);
1304   // Return promise
1305   return new promiseLibrary(function(resolve, reject) {
1306     exists(db, fileIdObject, rootCollection, options, function(err, r) {
1307       if(err) return reject(err);
1308       resolve(r);
1309     })
1310   });
1311 };
1312
1313 var exists = function(db, fileIdObject, rootCollection, options, callback) {
1314   // Establish read preference
1315   var readPreference = options.readPreference || ReadPreference.PRIMARY;
1316   // Fetch collection
1317   var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION;
1318   db.collection(rootCollectionFinal + ".files", function(err, collection) {
1319     if(err) return callback(err);
1320
1321     // Build query
1322     var query = (typeof fileIdObject == 'string' || Object.prototype.toString.call(fileIdObject) == '[object RegExp]' )
1323       ? {'filename':fileIdObject}
1324       : {'_id':fileIdObject};    // Attempt to locate file
1325
1326     // We have a specific query
1327     if(fileIdObject != null
1328       && typeof fileIdObject == 'object'
1329       && Object.prototype.toString.call(fileIdObject) != '[object RegExp]') {
1330       query = fileIdObject;
1331     }
1332
1333     // Check if the entry exists
1334     collection.findOne(query, {readPreference:readPreference}, function(err, item) {
1335       if(err) return callback(err);
1336       callback(null, item == null ? false : true);
1337     });
1338   });
1339 }
1340
1341 define.staticMethod('exist', {callback: true, promise:true});
1342
1343 /**
1344  * Gets the list of files stored in the GridFS.
1345  *
1346  * @method
1347  * @static
1348  * @param {Db} db the database to query.
1349  * @param {string} [rootCollection] The root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
1350  * @param {object} [options=null] Optional settings.
1351  * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
1352  * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
1353  * @param {GridStore~resultCallback} [callback] result from exists.
1354  * @return {Promise} returns Promise if no callback passed
1355  * @deprecated Use GridFSBucket API instead
1356  */
1357 GridStore.list = function(db, rootCollection, options, callback) {
1358   var args = Array.prototype.slice.call(arguments, 1);
1359   callback = args.pop();
1360   if(typeof callback != 'function') args.push(callback);
1361   rootCollection = args.length ? args.shift() : null;
1362   options = args.length ? args.shift() : {};
1363   options = options || {};
1364
1365   // Get the promiseLibrary
1366   var promiseLibrary = options.promiseLibrary;
1367
1368   // No promise library selected fall back
1369   if(!promiseLibrary) {
1370     promiseLibrary = typeof global.Promise == 'function' ?
1371       global.Promise : require('es6-promise').Promise;
1372   }
1373
1374   // We provided a callback leg
1375   if(typeof callback == 'function') return list(db, rootCollection, options, callback);
1376   // Return promise
1377   return new promiseLibrary(function(resolve, reject) {
1378     list(db, rootCollection, options, function(err, r) {
1379       if(err) return reject(err);
1380       resolve(r);
1381     })
1382   });
1383 };
1384
1385 var list = function(db, rootCollection, options, callback) {
1386   // Ensure we have correct values
1387   if(rootCollection != null && typeof rootCollection == 'object') {
1388     options = rootCollection;
1389     rootCollection = null;
1390   }
1391
1392   // Establish read preference
1393   var readPreference = options.readPreference || ReadPreference.PRIMARY;
1394   // Check if we are returning by id not filename
1395   var byId = options['id'] != null ? options['id'] : false;
1396   // Fetch item
1397   var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION;
1398   var items = [];
1399   db.collection((rootCollectionFinal + ".files"), function(err, collection) {
1400     if(err) return callback(err);
1401
1402     collection.find({}, {readPreference:readPreference}, function(err, cursor) {
1403       if(err) return callback(err);
1404
1405       cursor.each(function(err, item) {
1406         if(item != null) {
1407           items.push(byId ? item._id : item.filename);
1408         } else {
1409           callback(err, items);
1410         }
1411       });
1412     });
1413   });
1414 }
1415
1416 define.staticMethod('list', {callback: true, promise:true});
1417
1418 /**
1419  * Reads the contents of a file.
1420  *
1421  * This method has the following signatures
1422  *
1423  * (db, name, callback)
1424  * (db, name, length, callback)
1425  * (db, name, length, offset, callback)
1426  * (db, name, length, offset, options, callback)
1427  *
1428  * @method
1429  * @static
1430  * @param {Db} db the database to query.
1431  * @param {string} name The name of the file.
1432  * @param {number} [length] The size of data to read.
1433  * @param {number} [offset] The offset from the head of the file of which to start reading from.
1434  * @param {object} [options=null] Optional settings.
1435  * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
1436  * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
1437  * @param {GridStore~readCallback} [callback] the command callback.
1438  * @return {Promise} returns Promise if no callback passed
1439  * @deprecated Use GridFSBucket API instead
1440  */
1441 GridStore.read = function(db, name, length, offset, options, callback) {
1442   var args = Array.prototype.slice.call(arguments, 2);
1443   callback = args.pop();
1444   if(typeof callback != 'function') args.push(callback);
1445   length = args.length ? args.shift() : null;
1446   offset = args.length ? args.shift() : null;
1447   options = args.length ? args.shift() : null;
1448   options = options || {};
1449
1450   // Get the promiseLibrary
1451   var promiseLibrary = options ? options.promiseLibrary : null;
1452
1453   // No promise library selected fall back
1454   if(!promiseLibrary) {
1455     promiseLibrary = typeof global.Promise == 'function' ?
1456       global.Promise : require('es6-promise').Promise;
1457   }
1458
1459   // We provided a callback leg
1460   if(typeof callback == 'function') return readStatic(db, name, length, offset, options, callback);
1461   // Return promise
1462   return new promiseLibrary(function(resolve, reject) {
1463     readStatic(db, name, length, offset, options, function(err, r) {
1464       if(err) return reject(err);
1465       resolve(r);
1466     })
1467   });
1468 };
1469
1470 var readStatic = function(db, name, length, offset, options, callback) {
1471   new GridStore(db, name, "r", options).open(function(err, gridStore) {
1472     if(err) return callback(err);
1473     // Make sure we are not reading out of bounds
1474     if(offset && offset >= gridStore.length) return callback("offset larger than size of file", null);
1475     if(length && length > gridStore.length) return callback("length is larger than the size of the file", null);
1476     if(offset && length && (offset + length) > gridStore.length) return callback("offset and length is larger than the size of the file", null);
1477
1478     if(offset != null) {
1479       gridStore.seek(offset, function(err, gridStore) {
1480         if(err) return callback(err);
1481         gridStore.read(length, callback);
1482       });
1483     } else {
1484       gridStore.read(length, callback);
1485     }
1486   });
1487 }
1488
1489 define.staticMethod('read', {callback: true, promise:true});
1490
1491 /**
1492  * Read the entire file as a list of strings splitting by the provided separator.
1493  *
1494  * @method
1495  * @static
1496  * @param {Db} db the database to query.
1497  * @param {(String|object)} name the name of the file.
1498  * @param {string} [separator] The character to be recognized as the newline separator.
1499  * @param {object} [options=null] Optional settings.
1500  * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
1501  * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
1502  * @param {GridStore~readlinesCallback} [callback] the command callback.
1503  * @return {Promise} returns Promise if no callback passed
1504  * @deprecated Use GridFSBucket API instead
1505  */
1506 GridStore.readlines = function(db, name, separator, options, callback) {
1507   var args = Array.prototype.slice.call(arguments, 2);
1508   callback = args.pop();
1509   if(typeof callback != 'function') args.push(callback);
1510   separator = args.length ? args.shift() : null;
1511   options = args.length ? args.shift() : null;
1512   options = options || {};
1513
1514   // Get the promiseLibrary
1515   var promiseLibrary = options ? options.promiseLibrary : null;
1516
1517   // No promise library selected fall back
1518   if(!promiseLibrary) {
1519     promiseLibrary = typeof global.Promise == 'function' ?
1520       global.Promise : require('es6-promise').Promise;
1521   }
1522
1523   // We provided a callback leg
1524   if(typeof callback == 'function') return readlinesStatic(db, name, separator, options, callback);
1525   // Return promise
1526   return new promiseLibrary(function(resolve, reject) {
1527     readlinesStatic(db, name, separator, options, function(err, r) {
1528       if(err) return reject(err);
1529       resolve(r);
1530     })
1531   });
1532 };
1533
1534 var readlinesStatic = function(db, name, separator, options, callback) {
1535   var finalSeperator = separator == null ? "\n" : separator;
1536   new GridStore(db, name, "r", options).open(function(err, gridStore) {
1537     if(err) return callback(err);
1538     gridStore.readlines(finalSeperator, callback);
1539   });
1540 }
1541
1542 define.staticMethod('readlines', {callback: true, promise:true});
1543
1544 /**
1545  * Deletes the chunks and metadata information of a file from GridFS.
1546  *
1547  * @method
1548  * @static
1549  * @param {Db} db The database to query.
1550  * @param {(string|array)} names The name/names of the files to delete.
1551  * @param {object} [options=null] Optional settings.
1552  * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
1553  * @param {GridStore~resultCallback} [callback] the command callback.
1554  * @return {Promise} returns Promise if no callback passed
1555  * @deprecated Use GridFSBucket API instead
1556  */
1557 GridStore.unlink = function(db, names, options, callback) {
1558   var self = this;
1559   var args = Array.prototype.slice.call(arguments, 2);
1560   callback = args.pop();
1561   if(typeof callback != 'function') args.push(callback);
1562   options = args.length ? args.shift() : {};
1563   options = options || {};
1564
1565   // Get the promiseLibrary
1566   var promiseLibrary = options.promiseLibrary;
1567
1568   // No promise library selected fall back
1569   if(!promiseLibrary) {
1570     promiseLibrary = typeof global.Promise == 'function' ?
1571       global.Promise : require('es6-promise').Promise;
1572   }
1573
1574   // We provided a callback leg
1575   if(typeof callback == 'function') return unlinkStatic(self, db, names, options, callback);
1576
1577   // Return promise
1578   return new promiseLibrary(function(resolve, reject) {
1579     unlinkStatic(self, db, names, options, function(err, r) {
1580       if(err) return reject(err);
1581       resolve(r);
1582     })
1583   });
1584 };
1585
1586 var unlinkStatic = function(self, db, names, options, callback) {
1587   // Get the write concern
1588   var writeConcern = _getWriteConcern(db, options);
1589
1590   // List of names
1591   if(names.constructor == Array) {
1592     var tc = 0;
1593     for(var i = 0; i < names.length; i++) {
1594       ++tc;
1595       GridStore.unlink(db, names[i], options, function(result) {
1596         if(--tc == 0) {
1597             callback(null, self);
1598         }
1599       });
1600     }
1601   } else {
1602     new GridStore(db, names, "w", options).open(function(err, gridStore) {
1603       if(err) return callback(err);
1604       deleteChunks(gridStore, function(err, result) {
1605         if(err) return callback(err);
1606         gridStore.collection(function(err, collection) {
1607           if(err) return callback(err);
1608           collection.remove({'_id':gridStore.fileId}, writeConcern, function(err, result) {
1609             callback(err, self);
1610           });
1611         });
1612       });
1613     });
1614   }
1615 }
1616
1617 define.staticMethod('unlink', {callback: true, promise:true});
1618
1619 /**
1620  *  @ignore
1621  */
1622 var _writeNormal = function(self, data, close, callback) {
1623   // If we have a buffer write it using the writeBuffer method
1624   if(Buffer.isBuffer(data)) {
1625     return writeBuffer(self, data, close, callback);
1626   } else {
1627     return writeBuffer(self, new Buffer(data, 'binary'), close, callback);
1628   }
1629 }
1630
1631 /**
1632  * @ignore
1633  */
1634 var _setWriteConcernHash = function(options) {
1635   var finalOptions = {};
1636   if(options.w != null) finalOptions.w = options.w;
1637   if(options.journal == true) finalOptions.j = options.journal;
1638   if(options.j == true) finalOptions.j = options.j;
1639   if(options.fsync == true) finalOptions.fsync = options.fsync;
1640   if(options.wtimeout != null) finalOptions.wtimeout = options.wtimeout;
1641   return finalOptions;
1642 }
1643
1644 /**
1645  * @ignore
1646  */
1647 var _getWriteConcern = function(self, options) {
1648   // Final options
1649   var finalOptions = {w:1};
1650   options = options || {};
1651
1652   // Local options verification
1653   if(options.w != null || typeof options.j == 'boolean' || typeof options.journal == 'boolean' || typeof options.fsync == 'boolean') {
1654     finalOptions = _setWriteConcernHash(options);
1655   } else if(options.safe != null && typeof options.safe == 'object') {
1656     finalOptions = _setWriteConcernHash(options.safe);
1657   } else if(typeof options.safe == "boolean") {
1658     finalOptions = {w: (options.safe ? 1 : 0)};
1659   } else if(self.options.w != null || typeof self.options.j == 'boolean' || typeof self.options.journal == 'boolean' || typeof self.options.fsync == 'boolean') {
1660     finalOptions = _setWriteConcernHash(self.options);
1661   } else if(self.safe && (self.safe.w != null || typeof self.safe.j == 'boolean' || typeof self.safe.journal == 'boolean' || typeof self.safe.fsync == 'boolean')) {
1662     finalOptions = _setWriteConcernHash(self.safe);
1663   } else if(typeof self.safe == "boolean") {
1664     finalOptions = {w: (self.safe ? 1 : 0)};
1665   }
1666
1667   // Ensure we don't have an invalid combination of write concerns
1668   if(finalOptions.w < 1
1669     && (finalOptions.journal == true || finalOptions.j == true || finalOptions.fsync == true)) throw MongoError.create({message: "No acknowledgement using w < 1 cannot be combined with journal:true or fsync:true", driver:true});
1670
1671   // Return the options
1672   return finalOptions;
1673 }
1674
1675 /**
1676  * Create a new GridStoreStream instance (INTERNAL TYPE, do not instantiate directly)
1677  *
1678  * @class
1679  * @extends external:Duplex
1680  * @return {GridStoreStream} a GridStoreStream instance.
1681  * @deprecated Use GridFSBucket API instead
1682  */
1683 var GridStoreStream = function(gs) {
1684   var self = this;
1685   // Initialize the duplex stream
1686   Duplex.call(this);
1687
1688   // Get the gridstore
1689   this.gs = gs;
1690
1691   // End called
1692   this.endCalled = false;
1693
1694   // If we have a seek
1695   this.totalBytesToRead = this.gs.length - this.gs.position;
1696   this.seekPosition = this.gs.position;
1697 }
1698
1699 //
1700 // Inherit duplex
1701 inherits(GridStoreStream, Duplex);
1702
1703 GridStoreStream.prototype._pipe = GridStoreStream.prototype.pipe;
1704
1705 // Set up override
1706 GridStoreStream.prototype.pipe = function(destination) {
1707   var self = this;
1708
1709   // Only open gridstore if not already open
1710   if(!self.gs.isOpen) {
1711     self.gs.open(function(err) {
1712       if(err) return self.emit('error', err);
1713       self.totalBytesToRead = self.gs.length - self.gs.position;
1714       self._pipe.apply(self, [destination]);
1715     });
1716   } else {
1717     self.totalBytesToRead = self.gs.length - self.gs.position;
1718     self._pipe.apply(self, [destination]);
1719   }
1720
1721   return destination;
1722 }
1723
1724 // Called by stream
1725 GridStoreStream.prototype._read = function(n) {
1726   var self = this;
1727
1728   var read = function() {
1729     // Read data
1730     self.gs.read(length, function(err, buffer) {
1731       if(err && !self.endCalled) return self.emit('error', err);
1732
1733       // Stream is closed
1734       if(self.endCalled || buffer == null) return self.push(null);
1735       // Remove bytes read
1736       if(buffer.length <= self.totalBytesToRead) {
1737         self.totalBytesToRead = self.totalBytesToRead - buffer.length;
1738         self.push(buffer);
1739       } else if(buffer.length > self.totalBytesToRead) {
1740         self.totalBytesToRead = self.totalBytesToRead - buffer._index;
1741         self.push(buffer.slice(0, buffer._index));
1742       }
1743
1744       // Finished reading
1745       if(self.totalBytesToRead <= 0) {
1746         self.endCalled = true;
1747       }
1748     });
1749   }
1750
1751   // Set read length
1752   var length = self.gs.length < self.gs.chunkSize ? self.gs.length - self.seekPosition : self.gs.chunkSize;
1753   if(!self.gs.isOpen) {
1754     self.gs.open(function(err, gs) {
1755       self.totalBytesToRead = self.gs.length - self.gs.position;
1756       if(err) return self.emit('error', err);
1757       read();
1758     });
1759   } else {
1760     read();
1761   }
1762 }
1763
1764 GridStoreStream.prototype.destroy = function() {
1765   this.pause();
1766   this.endCalled = true;
1767   this.gs.close();
1768   this.emit('end');
1769 }
1770
1771 GridStoreStream.prototype.write = function(chunk, encoding, callback) {
1772   var self = this;
1773   if(self.endCalled) return self.emit('error', MongoError.create({message: 'attempting to write to stream after end called', driver:true}))
1774   // Do we have to open the gridstore
1775   if(!self.gs.isOpen) {
1776     self.gs.open(function() {
1777       self.gs.isOpen = true;
1778       self.gs.write(chunk, function() {
1779         process.nextTick(function() {
1780           self.emit('drain');
1781         });
1782       });
1783     });
1784     return false;
1785   } else {
1786     self.gs.write(chunk, function() {
1787       self.emit('drain');
1788     });
1789     return true;
1790   }
1791 }
1792
1793 GridStoreStream.prototype.end = function(chunk, encoding, callback) {
1794   var self = this;
1795   var args = Array.prototype.slice.call(arguments, 0);
1796   callback = args.pop();
1797   if(typeof callback != 'function') args.push(callback);
1798   chunk = args.length ? args.shift() : null;
1799   encoding = args.length ? args.shift() : null;
1800   self.endCalled = true;
1801
1802   if(chunk) {
1803     self.gs.write(chunk, function() {
1804       self.gs.close(function() {
1805         if(typeof callback == 'function') callback();
1806         self.emit('end')
1807       });
1808     });
1809   }
1810
1811   self.gs.close(function() {
1812     if(typeof callback == 'function') callback();
1813     self.emit('end')
1814   });
1815 }
1816
1817 /**
1818  * The read() method pulls some data out of the internal buffer and returns it. If there is no data available, then it will return null.
1819  * @function external:Duplex#read
1820  * @param {number} size Optional argument to specify how much data to read.
1821  * @return {(String | Buffer | null)}
1822  */
1823
1824 /**
1825  * Call this function to cause the stream to return strings of the specified encoding instead of Buffer objects.
1826  * @function external:Duplex#setEncoding
1827  * @param {string} encoding The encoding to use.
1828  * @return {null}
1829  */
1830
1831 /**
1832  * This method will cause the readable stream to resume emitting data events.
1833  * @function external:Duplex#resume
1834  * @return {null}
1835  */
1836
1837 /**
1838  * This method will cause a stream in flowing-mode to stop emitting data events. Any data that becomes available will remain in the internal buffer.
1839  * @function external:Duplex#pause
1840  * @return {null}
1841  */
1842
1843 /**
1844  * This method pulls all the data out of a readable stream, and writes it to the supplied destination, automatically managing the flow so that the destination is not overwhelmed by a fast readable stream.
1845  * @function external:Duplex#pipe
1846  * @param {Writable} destination The destination for writing data
1847  * @param {object} [options] Pipe options
1848  * @return {null}
1849  */
1850
1851 /**
1852  * This method will remove the hooks set up for a previous pipe() call.
1853  * @function external:Duplex#unpipe
1854  * @param {Writable} [destination] The destination for writing data
1855  * @return {null}
1856  */
1857
1858 /**
1859  * This is useful in certain cases where a stream is being consumed by a parser, which needs to "un-consume" some data that it has optimistically pulled out of the source, so that the stream can be passed on to some other party.
1860  * @function external:Duplex#unshift
1861  * @param {(Buffer|string)} chunk Chunk of data to unshift onto the read queue.
1862  * @return {null}
1863  */
1864
1865 /**
1866  * Versions of Node prior to v0.10 had streams that did not implement the entire Streams API as it is today. (See "Compatibility" below for more information.)
1867  * @function external:Duplex#wrap
1868  * @param {Stream} stream An "old style" readable stream.
1869  * @return {null}
1870  */
1871
1872 /**
1873  * This method writes some data to the underlying system, and calls the supplied callback once the data has been fully handled.
1874  * @function external:Duplex#write
1875  * @param {(string|Buffer)} chunk The data to write
1876  * @param {string} encoding The encoding, if chunk is a String
1877  * @param {function} callback Callback for when this chunk of data is flushed
1878  * @return {boolean}
1879  */
1880
1881 /**
1882  * Call this method when no more data will be written to the stream. If supplied, the callback is attached as a listener on the finish event.
1883  * @function external:Duplex#end
1884  * @param {(string|Buffer)} chunk The data to write
1885  * @param {string} encoding The encoding, if chunk is a String
1886  * @param {function} callback Callback for when this chunk of data is flushed
1887  * @return {null}
1888  */
1889
1890 /**
1891  * GridStoreStream stream data event, fired for each document in the cursor.
1892  *
1893  * @event GridStoreStream#data
1894  * @type {object}
1895  */
1896
1897 /**
1898  * GridStoreStream stream end event
1899  *
1900  * @event GridStoreStream#end
1901  * @type {null}
1902  */
1903
1904 /**
1905  * GridStoreStream stream close event
1906  *
1907  * @event GridStoreStream#close
1908  * @type {null}
1909  */
1910
1911 /**
1912  * GridStoreStream stream readable event
1913  *
1914  * @event GridStoreStream#readable
1915  * @type {null}
1916  */
1917
1918 /**
1919  * GridStoreStream stream drain event
1920  *
1921  * @event GridStoreStream#drain
1922  * @type {null}
1923  */
1924
1925 /**
1926  * GridStoreStream stream finish event
1927  *
1928  * @event GridStoreStream#finish
1929  * @type {null}
1930  */
1931
1932 /**
1933  * GridStoreStream stream pipe event
1934  *
1935  * @event GridStoreStream#pipe
1936  * @type {null}
1937  */
1938
1939 /**
1940  * GridStoreStream stream unpipe event
1941  *
1942  * @event GridStoreStream#unpipe
1943  * @type {null}
1944  */
1945
1946 /**
1947  * GridStoreStream stream error event
1948  *
1949  * @event GridStoreStream#error
1950  * @type {null}
1951  */
1952
1953 /**
1954  * @ignore
1955  */
1956 module.exports = GridStore;