3 var Binary = require('mongodb-core').BSON.Binary,
4 ObjectID = require('mongodb-core').BSON.ObjectID;
7 * Class for representing a single chunk in GridFS.
11 * @param file {GridStore} The {@link GridStore} object holding this chunk.
12 * @param mongoObject {object} The mongo object representation of this chunk.
14 * @throws Error when the type of data field for {@link mongoObject} is not
15 * supported. Currently supported types for data field are instances of
16 * {@link String}, {@link Array}, {@link Binary} and {@link Binary}
17 * from the bson module
19 * @see Chunk#buildMongoObject
21 var Chunk = function(file, mongoObject, writeConcern) {
22 if(!(this instanceof Chunk)) return new Chunk(file, mongoObject);
26 var mongoObjectFinal = mongoObject == null ? {} : mongoObject;
27 this.writeConcern = writeConcern || {w:1};
28 this.objectId = mongoObjectFinal._id == null ? new ObjectID() : mongoObjectFinal._id;
29 this.chunkNumber = mongoObjectFinal.n == null ? 0 : mongoObjectFinal.n;
30 this.data = new Binary();
32 if(mongoObjectFinal.data == null) {
33 } else if(typeof mongoObjectFinal.data == "string") {
34 var buffer = new Buffer(mongoObjectFinal.data.length);
35 buffer.write(mongoObjectFinal.data, 0, mongoObjectFinal.data.length, 'binary');
36 this.data = new Binary(buffer);
37 } else if(Array.isArray(mongoObjectFinal.data)) {
38 var buffer = new Buffer(mongoObjectFinal.data.length);
39 var data = mongoObjectFinal.data.join('');
40 buffer.write(data, 0, data.length, 'binary');
41 this.data = new Binary(buffer);
42 } else if(mongoObjectFinal.data._bsontype === 'Binary') {
43 this.data = mongoObjectFinal.data;
44 } else if(Buffer.isBuffer(mongoObjectFinal.data)) {
46 throw Error("Illegal chunk format");
50 this.internalPosition = 0;
54 * Writes a data to this object and advance the read/write head.
56 * @param data {string} the data to write
57 * @param callback {function(*, GridStore)} This will be called after executing
58 * this method. The first parameter will contain null and the second one
59 * will contain a reference to this object.
61 Chunk.prototype.write = function(data, callback) {
62 this.data.write(data, this.internalPosition, data.length, 'binary');
63 this.internalPosition = this.data.length();
64 if(callback != null) return callback(null, this);
69 * Reads data and advances the read/write head.
71 * @param length {number} The length of data to read.
73 * @return {string} The data read if the given length will not exceed the end of
74 * the chunk. Returns an empty String otherwise.
76 Chunk.prototype.read = function(length) {
77 // Default to full read if no index defined
78 length = length == null || length == 0 ? this.length() : length;
80 if(this.length() - this.internalPosition + 1 >= length) {
81 var data = this.data.read(this.internalPosition, length);
82 this.internalPosition = this.internalPosition + length;
89 Chunk.prototype.readSlice = function(length) {
90 if ((this.length() - this.internalPosition) >= length) {
92 if (this.data.buffer != null) { //Pure BSON
93 data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length);
94 } else { //Native BSON
95 data = new Buffer(length);
96 length = this.data.readInto(data, this.internalPosition);
98 this.internalPosition = this.internalPosition + length;
106 * Checks if the read/write head is at the end.
108 * @return {boolean} Whether the read/write head has reached the end of this
111 Chunk.prototype.eof = function() {
112 return this.internalPosition == this.length() ? true : false;
116 * Reads one character from the data of this chunk and advances the read/write
119 * @return {string} a single character data read if the the read/write head is
120 * not at the end of the chunk. Returns an empty String otherwise.
122 Chunk.prototype.getc = function() {
127 * Clears the contents of the data in this chunk and resets the read/write head
128 * to the initial position.
130 Chunk.prototype.rewind = function() {
131 this.internalPosition = 0;
132 this.data = new Binary();
136 * Saves this chunk to the database. Also overwrites existing entries having the
137 * same id as this chunk.
139 * @param callback {function(*, GridStore)} This will be called after executing
140 * this method. The first parameter will contain null and the second one
141 * will contain a reference to this object.
143 Chunk.prototype.save = function(options, callback) {
145 if(typeof options == 'function') {
150 self.file.chunkCollection(function(err, collection) {
151 if(err) return callback(err);
154 var writeOptions = { upsert: true };
155 for(var name in options) writeOptions[name] = options[name];
156 for(var name in self.writeConcern) writeOptions[name] = self.writeConcern[name];
158 if(self.data.length() > 0) {
159 self.buildMongoObject(function(mongoObject) {
160 var options = {forceServerObjectId:true};
161 for(var name in self.writeConcern) {
162 options[name] = self.writeConcern[name];
165 collection.replaceOne({'_id':self.objectId}, mongoObject, writeOptions, function(err, collection) {
170 callback(null, self);
177 * Creates a mongoDB object representation of this chunk.
179 * @param callback {function(Object)} This will be called after executing this
180 * method. The object will be passed to the first parameter and will have
185 * '_id' : , // {number} id for this chunk
186 * 'files_id' : , // {number} foreign key to the file collection
187 * 'n' : , // {number} chunk number
188 * 'data' : , // {bson#Binary} the chunk data itself
192 * @see <a href="http://www.mongodb.org/display/DOCS/GridFS+Specification#GridFSSpecification-{{chunks}}">MongoDB GridFS Chunk Object Structure</a>
194 Chunk.prototype.buildMongoObject = function(callback) {
196 'files_id': this.file.fileId,
197 'n': this.chunkNumber,
199 // If we are saving using a specific ObjectId
200 if(this.objectId != null) mongoObject._id = this.objectId;
202 callback(mongoObject);
206 * @return {number} the length of the data
208 Chunk.prototype.length = function() {
209 return this.data.length();
213 * The position of the read/write head
218 Object.defineProperty(Chunk.prototype, "position", { enumerable: true
220 return this.internalPosition;
222 , set: function(value) {
223 this.internalPosition = value;
228 * The default chunk size
231 Chunk.DEFAULT_CHUNK_SIZE = 1024 * 255;
233 module.exports = Chunk;