You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							237 lines
						
					
					
						
							7.0 KiB
						
					
					
				
			
		
		
	
	
							237 lines
						
					
					
						
							7.0 KiB
						
					
					
				| 'use strict';
 | |
| 
 | |
| var Binary = require('../core').BSON.Binary,
 | |
|   ObjectID = require('../core').BSON.ObjectID;
 | |
| 
 | |
| var Buffer = require('safe-buffer').Buffer;
 | |
| 
 | |
| /**
 | |
|  * Class for representing a single chunk in GridFS.
 | |
|  *
 | |
|  * @class
 | |
|  *
 | |
|  * @param file {GridStore} The {@link GridStore} object holding this chunk.
 | |
|  * @param mongoObject {object} The mongo object representation of this chunk.
 | |
|  *
 | |
|  * @throws Error when the type of data field for {@link mongoObject} is not
 | |
|  *     supported. Currently supported types for data field are instances of
 | |
|  *     {@link String}, {@link Array}, {@link Binary} and {@link Binary}
 | |
|  *     from the bson module
 | |
|  *
 | |
|  * @see Chunk#buildMongoObject
 | |
|  */
 | |
| var Chunk = function(file, mongoObject, writeConcern) {
 | |
|   if (!(this instanceof Chunk)) return new Chunk(file, mongoObject);
 | |
| 
 | |
|   this.file = file;
 | |
|   var mongoObjectFinal = mongoObject == null ? {} : mongoObject;
 | |
|   this.writeConcern = writeConcern || { w: 1 };
 | |
|   this.objectId = mongoObjectFinal._id == null ? new ObjectID() : mongoObjectFinal._id;
 | |
|   this.chunkNumber = mongoObjectFinal.n == null ? 0 : mongoObjectFinal.n;
 | |
|   this.data = new Binary();
 | |
| 
 | |
|   if (typeof mongoObjectFinal.data === 'string') {
 | |
|     var buffer = Buffer.alloc(mongoObjectFinal.data.length);
 | |
|     buffer.write(mongoObjectFinal.data, 0, mongoObjectFinal.data.length, 'binary');
 | |
|     this.data = new Binary(buffer);
 | |
|   } else if (Array.isArray(mongoObjectFinal.data)) {
 | |
|     buffer = Buffer.alloc(mongoObjectFinal.data.length);
 | |
|     var data = mongoObjectFinal.data.join('');
 | |
|     buffer.write(data, 0, data.length, 'binary');
 | |
|     this.data = new Binary(buffer);
 | |
|   } else if (mongoObjectFinal.data && mongoObjectFinal.data._bsontype === 'Binary') {
 | |
|     this.data = mongoObjectFinal.data;
 | |
|   } else if (!Buffer.isBuffer(mongoObjectFinal.data) && !(mongoObjectFinal.data == null)) {
 | |
|     throw Error('Illegal chunk format');
 | |
|   }
 | |
| 
 | |
|   // Update position
 | |
|   this.internalPosition = 0;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Writes a data to this object and advance the read/write head.
 | |
|  *
 | |
|  * @param data {string} the data to write
 | |
|  * @param callback {function(*, GridStore)} 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.
 | |
|  */
 | |
| Chunk.prototype.write = function(data, callback) {
 | |
|   this.data.write(data, this.internalPosition, data.length, 'binary');
 | |
|   this.internalPosition = this.data.length();
 | |
|   if (callback != null) return callback(null, this);
 | |
|   return this;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Reads data and advances the read/write head.
 | |
|  *
 | |
|  * @param length {number} The length of data to read.
 | |
|  *
 | |
|  * @return {string} The data read if the given length will not exceed the end of
 | |
|  *     the chunk. Returns an empty String otherwise.
 | |
|  */
 | |
| Chunk.prototype.read = function(length) {
 | |
|   // Default to full read if no index defined
 | |
|   length = length == null || length === 0 ? this.length() : length;
 | |
| 
 | |
|   if (this.length() - this.internalPosition + 1 >= length) {
 | |
|     var data = this.data.read(this.internalPosition, length);
 | |
|     this.internalPosition = this.internalPosition + length;
 | |
|     return data;
 | |
|   } else {
 | |
|     return '';
 | |
|   }
 | |
| };
 | |
| 
 | |
| Chunk.prototype.readSlice = function(length) {
 | |
|   if (this.length() - this.internalPosition >= length) {
 | |
|     var data = null;
 | |
|     if (this.data.buffer != null) {
 | |
|       //Pure BSON
 | |
|       data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length);
 | |
|     } else {
 | |
|       //Native BSON
 | |
|       data = Buffer.alloc(length);
 | |
|       length = this.data.readInto(data, this.internalPosition);
 | |
|     }
 | |
|     this.internalPosition = this.internalPosition + length;
 | |
|     return data;
 | |
|   } else {
 | |
|     return null;
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Checks if the read/write head is at the end.
 | |
|  *
 | |
|  * @return {boolean} Whether the read/write head has reached the end of this
 | |
|  *     chunk.
 | |
|  */
 | |
| Chunk.prototype.eof = function() {
 | |
|   return this.internalPosition === this.length() ? true : false;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Reads one character from the data of this chunk and advances the read/write
 | |
|  * head.
 | |
|  *
 | |
|  * @return {string} a single character data read if the the read/write head is
 | |
|  *     not at the end of the chunk. Returns an empty String otherwise.
 | |
|  */
 | |
| Chunk.prototype.getc = function() {
 | |
|   return this.read(1);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Clears the contents of the data in this chunk and resets the read/write head
 | |
|  * to the initial position.
 | |
|  */
 | |
| Chunk.prototype.rewind = function() {
 | |
|   this.internalPosition = 0;
 | |
|   this.data = new Binary();
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Saves this chunk to the database. Also overwrites existing entries having the
 | |
|  * same id as this chunk.
 | |
|  *
 | |
|  * @param callback {function(*, GridStore)} 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.
 | |
|  */
 | |
| Chunk.prototype.save = function(options, callback) {
 | |
|   var self = this;
 | |
|   if (typeof options === 'function') {
 | |
|     callback = options;
 | |
|     options = {};
 | |
|   }
 | |
| 
 | |
|   self.file.chunkCollection(function(err, collection) {
 | |
|     if (err) return callback(err);
 | |
| 
 | |
|     // Merge the options
 | |
|     var writeOptions = { upsert: true };
 | |
|     for (var name in options) writeOptions[name] = options[name];
 | |
|     for (name in self.writeConcern) writeOptions[name] = self.writeConcern[name];
 | |
| 
 | |
|     if (self.data.length() > 0) {
 | |
|       self.buildMongoObject(function(mongoObject) {
 | |
|         var options = { forceServerObjectId: true };
 | |
|         for (var name in self.writeConcern) {
 | |
|           options[name] = self.writeConcern[name];
 | |
|         }
 | |
| 
 | |
|         collection.replaceOne({ _id: self.objectId }, mongoObject, writeOptions, function(err) {
 | |
|           callback(err, self);
 | |
|         });
 | |
|       });
 | |
|     } else {
 | |
|       callback(null, self);
 | |
|     }
 | |
|     // });
 | |
|   });
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Creates a mongoDB object representation of this chunk.
 | |
|  *
 | |
|  * @param callback {function(Object)} This will be called after executing this
 | |
|  *     method. The object will be passed to the first parameter and will have
 | |
|  *     the structure:
 | |
|  *
 | |
|  *        <pre><code>
 | |
|  *        {
 | |
|  *          '_id' : , // {number} id for this chunk
 | |
|  *          'files_id' : , // {number} foreign key to the file collection
 | |
|  *          'n' : , // {number} chunk number
 | |
|  *          'data' : , // {bson#Binary} the chunk data itself
 | |
|  *        }
 | |
|  *        </code></pre>
 | |
|  *
 | |
|  * @see <a href="http://www.mongodb.org/display/DOCS/GridFS+Specification#GridFSSpecification-{{chunks}}">MongoDB GridFS Chunk Object Structure</a>
 | |
|  */
 | |
| Chunk.prototype.buildMongoObject = function(callback) {
 | |
|   var mongoObject = {
 | |
|     files_id: this.file.fileId,
 | |
|     n: this.chunkNumber,
 | |
|     data: this.data
 | |
|   };
 | |
|   // If we are saving using a specific ObjectId
 | |
|   if (this.objectId != null) mongoObject._id = this.objectId;
 | |
| 
 | |
|   callback(mongoObject);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * @return {number} the length of the data
 | |
|  */
 | |
| Chunk.prototype.length = function() {
 | |
|   return this.data.length();
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * The position of the read/write head
 | |
|  * @name position
 | |
|  * @lends Chunk#
 | |
|  * @field
 | |
|  */
 | |
| Object.defineProperty(Chunk.prototype, 'position', {
 | |
|   enumerable: true,
 | |
|   get: function() {
 | |
|     return this.internalPosition;
 | |
|   },
 | |
|   set: function(value) {
 | |
|     this.internalPosition = value;
 | |
|   }
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * The default chunk size
 | |
|  * @constant
 | |
|  */
 | |
| Chunk.DEFAULT_CHUNK_SIZE = 1024 * 255;
 | |
| 
 | |
| module.exports = Chunk;
 |