chunk.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. "use strict";
  2. var Binary = require('mongodb-core').BSON.Binary,
  3. ObjectID = require('mongodb-core').BSON.ObjectID;
  4. /**
  5. * Class for representing a single chunk in GridFS.
  6. *
  7. * @class
  8. *
  9. * @param file {GridStore} The {@link GridStore} object holding this chunk.
  10. * @param mongoObject {object} The mongo object representation of this chunk.
  11. *
  12. * @throws Error when the type of data field for {@link mongoObject} is not
  13. * supported. Currently supported types for data field are instances of
  14. * {@link String}, {@link Array}, {@link Binary} and {@link Binary}
  15. * from the bson module
  16. *
  17. * @see Chunk#buildMongoObject
  18. */
  19. var Chunk = function(file, mongoObject, writeConcern) {
  20. if(!(this instanceof Chunk)) return new Chunk(file, mongoObject);
  21. this.file = file;
  22. var self = this;
  23. var mongoObjectFinal = mongoObject == null ? {} : mongoObject;
  24. this.writeConcern = writeConcern || {w:1};
  25. this.objectId = mongoObjectFinal._id == null ? new ObjectID() : mongoObjectFinal._id;
  26. this.chunkNumber = mongoObjectFinal.n == null ? 0 : mongoObjectFinal.n;
  27. this.data = new Binary();
  28. if(mongoObjectFinal.data == null) {
  29. } else if(typeof mongoObjectFinal.data == "string") {
  30. var buffer = new Buffer(mongoObjectFinal.data.length);
  31. buffer.write(mongoObjectFinal.data, 0, mongoObjectFinal.data.length, 'binary');
  32. this.data = new Binary(buffer);
  33. } else if(Array.isArray(mongoObjectFinal.data)) {
  34. var buffer = new Buffer(mongoObjectFinal.data.length);
  35. var data = mongoObjectFinal.data.join('');
  36. buffer.write(data, 0, data.length, 'binary');
  37. this.data = new Binary(buffer);
  38. } else if(mongoObjectFinal.data._bsontype === 'Binary') {
  39. this.data = mongoObjectFinal.data;
  40. } else if(Buffer.isBuffer(mongoObjectFinal.data)) {
  41. } else {
  42. throw Error("Illegal chunk format");
  43. }
  44. // Update position
  45. this.internalPosition = 0;
  46. };
  47. /**
  48. * Writes a data to this object and advance the read/write head.
  49. *
  50. * @param data {string} the data to write
  51. * @param callback {function(*, GridStore)} This will be called after executing
  52. * this method. The first parameter will contain null and the second one
  53. * will contain a reference to this object.
  54. */
  55. Chunk.prototype.write = function(data, callback) {
  56. this.data.write(data, this.internalPosition, data.length, 'binary');
  57. this.internalPosition = this.data.length();
  58. if(callback != null) return callback(null, this);
  59. return this;
  60. };
  61. /**
  62. * Reads data and advances the read/write head.
  63. *
  64. * @param length {number} The length of data to read.
  65. *
  66. * @return {string} The data read if the given length will not exceed the end of
  67. * the chunk. Returns an empty String otherwise.
  68. */
  69. Chunk.prototype.read = function(length) {
  70. // Default to full read if no index defined
  71. length = length == null || length == 0 ? this.length() : length;
  72. if(this.length() - this.internalPosition + 1 >= length) {
  73. var data = this.data.read(this.internalPosition, length);
  74. this.internalPosition = this.internalPosition + length;
  75. return data;
  76. } else {
  77. return '';
  78. }
  79. };
  80. Chunk.prototype.readSlice = function(length) {
  81. if ((this.length() - this.internalPosition) >= length) {
  82. var data = null;
  83. if (this.data.buffer != null) { //Pure BSON
  84. data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length);
  85. } else { //Native BSON
  86. data = new Buffer(length);
  87. length = this.data.readInto(data, this.internalPosition);
  88. }
  89. this.internalPosition = this.internalPosition + length;
  90. return data;
  91. } else {
  92. return null;
  93. }
  94. };
  95. /**
  96. * Checks if the read/write head is at the end.
  97. *
  98. * @return {boolean} Whether the read/write head has reached the end of this
  99. * chunk.
  100. */
  101. Chunk.prototype.eof = function() {
  102. return this.internalPosition == this.length() ? true : false;
  103. };
  104. /**
  105. * Reads one character from the data of this chunk and advances the read/write
  106. * head.
  107. *
  108. * @return {string} a single character data read if the the read/write head is
  109. * not at the end of the chunk. Returns an empty String otherwise.
  110. */
  111. Chunk.prototype.getc = function() {
  112. return this.read(1);
  113. };
  114. /**
  115. * Clears the contents of the data in this chunk and resets the read/write head
  116. * to the initial position.
  117. */
  118. Chunk.prototype.rewind = function() {
  119. this.internalPosition = 0;
  120. this.data = new Binary();
  121. };
  122. /**
  123. * Saves this chunk to the database. Also overwrites existing entries having the
  124. * same id as this chunk.
  125. *
  126. * @param callback {function(*, GridStore)} This will be called after executing
  127. * this method. The first parameter will contain null and the second one
  128. * will contain a reference to this object.
  129. */
  130. Chunk.prototype.save = function(options, callback) {
  131. var self = this;
  132. if(typeof options == 'function') {
  133. callback = options;
  134. options = {};
  135. }
  136. self.file.chunkCollection(function(err, collection) {
  137. if(err) return callback(err);
  138. // Merge the options
  139. var writeOptions = {};
  140. for(var name in options) writeOptions[name] = options[name];
  141. for(var name in self.writeConcern) writeOptions[name] = self.writeConcern[name];
  142. // collection.remove({'_id':self.objectId}, self.writeConcern, function(err, result) {
  143. collection.remove({'_id':self.objectId}, writeOptions, function(err, result) {
  144. if(err) return callback(err);
  145. if(self.data.length() > 0) {
  146. self.buildMongoObject(function(mongoObject) {
  147. var options = {forceServerObjectId:true};
  148. for(var name in self.writeConcern) {
  149. options[name] = self.writeConcern[name];
  150. }
  151. collection.insert(mongoObject, writeOptions, function(err, collection) {
  152. callback(err, self);
  153. });
  154. });
  155. } else {
  156. callback(null, self);
  157. }
  158. });
  159. });
  160. };
  161. /**
  162. * Creates a mongoDB object representation of this chunk.
  163. *
  164. * @param callback {function(Object)} This will be called after executing this
  165. * method. The object will be passed to the first parameter and will have
  166. * the structure:
  167. *
  168. * <pre><code>
  169. * {
  170. * '_id' : , // {number} id for this chunk
  171. * 'files_id' : , // {number} foreign key to the file collection
  172. * 'n' : , // {number} chunk number
  173. * 'data' : , // {bson#Binary} the chunk data itself
  174. * }
  175. * </code></pre>
  176. *
  177. * @see <a href="http://www.mongodb.org/display/DOCS/GridFS+Specification#GridFSSpecification-{{chunks}}">MongoDB GridFS Chunk Object Structure</a>
  178. */
  179. Chunk.prototype.buildMongoObject = function(callback) {
  180. var mongoObject = {
  181. 'files_id': this.file.fileId,
  182. 'n': this.chunkNumber,
  183. 'data': this.data};
  184. // If we are saving using a specific ObjectId
  185. if(this.objectId != null) mongoObject._id = this.objectId;
  186. callback(mongoObject);
  187. };
  188. /**
  189. * @return {number} the length of the data
  190. */
  191. Chunk.prototype.length = function() {
  192. return this.data.length();
  193. };
  194. /**
  195. * The position of the read/write head
  196. * @name position
  197. * @lends Chunk#
  198. * @field
  199. */
  200. Object.defineProperty(Chunk.prototype, "position", { enumerable: true
  201. , get: function () {
  202. return this.internalPosition;
  203. }
  204. , set: function(value) {
  205. this.internalPosition = value;
  206. }
  207. });
  208. /**
  209. * The default chunk size
  210. * @constant
  211. */
  212. Chunk.DEFAULT_CHUNK_SIZE = 1024 * 255;
  213. module.exports = Chunk;