Class: GridFS::GridStore

Inherits:
Object show all
Includes:
Enumerable
Defined in:
lib/mongo/gridfs/grid_store.rb

Overview

GridStore is an IO-like class that provides input and output for streams of data to MongoDB.

Examples:


include GridFS

#Store the text "Hello, world!" in the grid store.
GridStore.open(database, 'filename', 'w') do |f|
  f.puts "Hello, world!"
end

# Output "Hello, world!"
GridStore.open(database, 'filename', 'r') do |f|
  puts f.read
end

# Add text to the grid store.
GridStore.open(database, 'filename', 'w+') do |f|
  f.puts "But wait, there's more!"
end

# Retrieve everything, outputting  "Hello, world!\nBut wait, there's more!\n"
GridStore.open(database, 'filename', 'r') do |f|
  puts f.read
end

Constant Summary collapse

DEFAULT_ROOT_COLLECTION =
'fs'
DEFAULT_CONTENT_TYPE =
'text/plain'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db, name, mode = 'r', options = {}) ⇒ GridStore

Initialize a GridStore instance for reading, writing, or modifying a given file. Note that it’s often easier to work with the various GridStore class methods (open, read, etc.).

Parameters:

  • db (Mongo::DB)

    a MongoDB database.

  • name (String)

    a filename.

  • mode (String) (defaults to: 'r')

    either ‘r’, ‘w’, or ‘w+’ for reading, writing, or appending, respectively.

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :root (String)

    DEFAULT_ROOT_COLLECTION (‘r’, ‘w’, ‘w+’) the name of the root collection to use.

  • :metadata (String) — default: {}

    (w, w+) A hash containing any data you want persisted as this file’s metadata.

  • :chunk_size (Integer) — default: Chunk::DEFAULT_CHUNK_SIZE

    (w) Sets chunk size for files opened for writing. See also GridStore#chunk_size=.

  • :content_type (String) — default: 'text/plain'

    Set the content type stored as the file’s metadata. See also GridStore#content_type=.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/mongo/gridfs/grid_store.rb', line 202

def initialize(db, name, mode='r', options={})
  @db, @filename, @mode = db, name, mode
  @root = options[:root] || DEFAULT_ROOT_COLLECTION

  doc = collection.find({'filename' => @filename}).next_document
  if doc
    @files_id = doc['_id']
    @content_type = doc['contentType']
    @chunk_size = doc['chunkSize']
    @upload_date = doc['uploadDate']
    @aliases = doc['aliases']
    @length = doc['length']
    @metadata = doc['metadata']
    @md5 = doc['md5']
  else
    @files_id = Mongo::ObjectID.new
    @content_type = DEFAULT_CONTENT_TYPE
    @chunk_size = Chunk::DEFAULT_CHUNK_SIZE
    @length = 0
  end

  case mode
  when 'r'
    @curr_chunk = nth_chunk(0)
    @position = 0
  when 'w'
    chunk_collection.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
    delete_chunks
    @curr_chunk = Chunk.new(self, 'n' => 0)
    @content_type = options[:content_type] if options[:content_type]
    @chunk_size = options[:chunk_size] if options[:chunk_size]
    @metadata = options[:metadata] if options[:metadata]
    @position = 0
  when 'w+'
    chunk_collection.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
    @curr_chunk = nth_chunk(last_chunk_number) || Chunk.new(self, 'n' => 0) # might be empty
    @curr_chunk.pos = @curr_chunk.data.length if @curr_chunk
    @metadata = options[:metadata] if options[:metadata]
    @position = @length
  else
    raise "error: illegal mode #{mode}"
  end

  @lineno = 0
  @pushback_byte = nil
end

Instance Attribute Details

#aliasesObject

Array of strings; may be nil



59
60
61
# File 'lib/mongo/gridfs/grid_store.rb', line 59

def aliases
  @aliases
end

#chunk_sizeObject

Returns the value of attribute chunk_size.



74
75
76
# File 'lib/mongo/gridfs/grid_store.rb', line 74

def chunk_size
  @chunk_size
end

#content_typeObject

Default is DEFAULT_CONTENT_TYPE



62
63
64
# File 'lib/mongo/gridfs/grid_store.rb', line 62

def content_type
  @content_type
end

#filenameObject

Returns the value of attribute filename.



56
57
58
# File 'lib/mongo/gridfs/grid_store.rb', line 56

def filename
  @filename
end

#files_idObject (readonly)

Returns the value of attribute files_id.



69
70
71
# File 'lib/mongo/gridfs/grid_store.rb', line 69

def files_id
  @files_id
end

#lengthObject (readonly)

Size of file in bytes



65
66
67
# File 'lib/mongo/gridfs/grid_store.rb', line 65

def length
  @length
end

#linenoObject

Returns the value of attribute lineno.



76
77
78
# File 'lib/mongo/gridfs/grid_store.rb', line 76

def lineno
  @lineno
end

#md5Object (readonly)

Returns the value of attribute md5.



78
79
80
# File 'lib/mongo/gridfs/grid_store.rb', line 78

def md5
  @md5
end

#metadataObject

Returns the value of attribute metadata.



67
68
69
# File 'lib/mongo/gridfs/grid_store.rb', line 67

def 
  @metadata
end

#upload_dateObject (readonly)

Time that the file was first saved.



72
73
74
# File 'lib/mongo/gridfs/grid_store.rb', line 72

def upload_date
  @upload_date
end

Class Method Details

.exist?(db, name, root_collection = DEFAULT_ROOT_COLLECTION) ⇒ Boolean

Determine whether a given file exists in the GridStore.

Parameters:

  • a (Mongo::DB)

    MongoDB database.

  • name (String)

    the filename.

  • root_collection (String) (defaults to: DEFAULT_ROOT_COLLECTION)

    the name of the gridfs root collection.

Returns:

  • (Boolean)


87
88
89
# File 'lib/mongo/gridfs/grid_store.rb', line 87

def self.exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION)
  db.collection("#{root_collection}.files").find({'filename' => name}).next_document != nil
end

.list(db, root_collection = DEFAULT_ROOT_COLLECTION) ⇒ Array

List the contents of all GridFS files stored in the given db and root collection.

Parameters:

  • db (Mongo::DB)

    a MongoDB database.

  • root_collection (String) (defaults to: DEFAULT_ROOT_COLLECTION)

    the name of the root collection.

Returns:

  • (Array)


137
138
139
140
141
# File 'lib/mongo/gridfs/grid_store.rb', line 137

def self.list(db, root_collection=DEFAULT_ROOT_COLLECTION)
  db.collection("#{root_collection}.files").find().map do |f|
    f['filename']
  end
end

.mv(db, src, dest, root_collection = DEFAULT_ROOT_COLLECTION) ⇒ Object

Rename a file in this collection. Note that this method uses Collection#update, which means that you will not be notified

Parameters:

  • a (Mongo::DB)

    MongoDB database.

  • src (String)

    the name of the source file.

  • dest (String)

    the name of the destination file.

  • root_collection (String) (defaults to: DEFAULT_ROOT_COLLECTION)

    the name of the default root collection.



181
182
183
# File 'lib/mongo/gridfs/grid_store.rb', line 181

def self.mv(db, src, dest, root_collection=DEFAULT_ROOT_COLLECTION)
  db.collection("#{root_collection}.files").update({ :filename => src }, { '$set' => { :filename => dest } })
end

.open(db, name, mode, options = {}) ⇒ Object

Open a GridFS file for reading, writing, or appending. Note that this method must be used with a block.

Parameters:

  • a (Mongo::DB)

    MongoDB database.

  • name (String)

    the filename.

  • mode (String)

    one of ‘r’, ‘w’, or ‘w+’ for reading, writing, and appending, respectively.

  • options (Hash) (defaults to: {})

    any of the options available on GridStore initialization.

See Also:

  • #initialize
  • various GridStore class methods, e.g., GridStore.open, GridStore.read etc.


103
104
105
106
107
108
109
110
111
112
# File 'lib/mongo/gridfs/grid_store.rb', line 103

def self.open(db, name, mode, options={})
  gs = self.new(db, name, mode, options)
  result = nil
  begin
    result = yield gs if block_given?
  ensure
    gs.close
  end
  result
end

.read(db, name, length = nil, offset = nil) ⇒ String

Read a file stored in GridFS.

Parameters:

  • db (Mongo::DB)

    a MongoDB database.

  • name (String)

    the name of the file.

  • length (Integer) (defaults to: nil)

    the number of bytes to read.

  • offset (Integer) (defaults to: nil)

    the number of bytes beyond the beginning of the file to start reading.

Returns:

  • (String)

    the file data



123
124
125
126
127
128
# File 'lib/mongo/gridfs/grid_store.rb', line 123

def self.read(db, name, length=nil, offset=nil)
  GridStore.open(db, name, 'r') do |gs|
    gs.seek(offset) if offset
    gs.read(length)
  end
end

.readlines(db, name, separator = $/) ⇒ Array

Get each line of data from the specified file as an array of strings.

Parameters:

  • db (Mongo::DB)

    a MongoDB database.

  • name (String)

    the filename.

  • separator (String, Reg) (defaults to: $/)

Returns:

  • (Array)


151
152
153
154
155
# File 'lib/mongo/gridfs/grid_store.rb', line 151

def self.readlines(db, name, separator=$/)
  GridStore.open(db, name, 'r') do |gs|
    gs.readlines(separator)
  end
end

Remove one for more files from the given db.

Parameters:

  • db (Mongo::Database)

    a MongoDB database.

  • names (Array<String>)

    the filenames to remove

Returns:

  • (True)


163
164
165
166
167
168
169
# File 'lib/mongo/gridfs/grid_store.rb', line 163

def self.unlink(db, *names)
  names.each do |name|
    gs = GridStore.new(db, name)
    gs.send(:delete_chunks)
    gs.collection.remove('_id' => gs.files_id)
  end
end

Instance Method Details

#<<(obj) ⇒ Object



389
390
391
# File 'lib/mongo/gridfs/grid_store.rb', line 389

def <<(obj)
  write(obj.to_s)
end

#chunk_collectionMongo::Collection

Get the chunk collection referenced by this GridStore.

Returns:



259
260
261
# File 'lib/mongo/gridfs/grid_store.rb', line 259

def chunk_collection
  @db.collection("#{@root}.chunks")
end

#closeObject


closing ================

+++



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'lib/mongo/gridfs/grid_store.rb', line 470

def close
  if @mode[0] == ?w
    if @curr_chunk
      @curr_chunk.truncate
      @curr_chunk.save if @curr_chunk.pos > 0
    end
    files = collection
    if @upload_date
      files.remove('_id' => @files_id)
    else
      @upload_date = Time.now
    end
    files.insert(to_mongo_object)
  end
  @db = nil
end

#closed?Boolean

Returns:

  • (Boolean)


487
488
489
# File 'lib/mongo/gridfs/grid_store.rb', line 487

def closed?
  @db == nil
end

#collectionMongo::Collection

Get the files collection referenced by this GridStore instance.

Returns:



252
253
254
# File 'lib/mongo/gridfs/grid_store.rb', line 252

def collection
  @db.collection("#{@root}.files")
end

#eachObject Also known as: each_line



333
334
335
336
337
338
339
# File 'lib/mongo/gridfs/grid_store.rb', line 333

def each
  line = gets
  while line
    yield line
    line = gets
  end
end

#each_byteObject



342
343
344
345
346
347
348
# File 'lib/mongo/gridfs/grid_store.rb', line 342

def each_byte
  byte = self.getc
  while byte
    yield byte
    byte = self.getc
  end
end

#eofObject Also known as: eof?

status ================

Raises:

  • (IOError)


420
421
422
423
# File 'lib/mongo/gridfs/grid_store.rb', line 420

def eof
  raise IOError.new("stream not open for reading") unless @mode[0] == ?r
  @position >= @length
end

#flushObject

A no-op.



415
416
# File 'lib/mongo/gridfs/grid_store.rb', line 415

def flush
end

#getcObject

reading ================


278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/mongo/gridfs/grid_store.rb', line 278

def getc
  if @pushback_byte
    byte = @pushback_byte
    @pushback_byte = nil
    @position += 1
    byte
  elsif eof?
    nil
  else
    if @curr_chunk.eof?
      @curr_chunk = nth_chunk(@curr_chunk.chunk_number + 1)
    end
    @position += 1
    @curr_chunk.getc
  end
end

#gets(separator = $/) ⇒ Object



295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/mongo/gridfs/grid_store.rb', line 295

def gets(separator=$/)
  str = ''
  byte = self.getc
  return nil if byte == nil # EOF
  while byte != nil
    s = byte.chr
    str << s
    break if s == separator
    byte = self.getc
  end
  @lineno += 1
  str
end


367
368
369
370
371
372
373
374
# File 'lib/mongo/gridfs/grid_store.rb', line 367

def print(*objs)
  objs = [$_] if objs == nil || objs.empty?
  objs.each { |obj|
    str = obj.to_s
    str.each_byte { |byte| self.putc(byte) }
  }
  nil
end

#putc(byte) ⇒ Object

writing ================


357
358
359
360
361
362
363
364
365
# File 'lib/mongo/gridfs/grid_store.rb', line 357

def putc(byte)
  if @curr_chunk.pos == @chunk_size
    prev_chunk_number = @curr_chunk.chunk_number
    @curr_chunk.save
    @curr_chunk = Chunk.new(self, 'n' => prev_chunk_number + 1)
  end
  @position += 1
  @curr_chunk.putc(byte)
end

#puts(*objs) ⇒ Object



376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/mongo/gridfs/grid_store.rb', line 376

def puts(*objs)
  if objs == nil || objs.empty?
    self.putc(10)
  else
    print(*objs.collect{ |obj|
            str = obj.to_s
            str << "\n" unless str =~ /\n$/
            str
          })
  end
  nil
end

#read(len = nil, buf = nil) ⇒ Object



309
310
311
312
313
314
315
# File 'lib/mongo/gridfs/grid_store.rb', line 309

def read(len=nil, buf=nil)
  if len
    read_partial(len, buf)
  else
    read_all(buf)
  end
end

#readcharObject

Raises:

  • (EOFError)


317
318
319
320
321
# File 'lib/mongo/gridfs/grid_store.rb', line 317

def readchar
  byte = self.getc
  raise EOFError.new if byte == nil
  byte
end

#readline(separator = $/) ⇒ Object

Raises:

  • (EOFError)


323
324
325
326
327
# File 'lib/mongo/gridfs/grid_store.rb', line 323

def readline(separator=$/)
  line = gets
  raise EOFError.new if line == nil
  line
end

#readlines(separator = $/) ⇒ Object



329
330
331
# File 'lib/mongo/gridfs/grid_store.rb', line 329

def readlines(separator=$/)
  read.split(separator).collect { |line| "#{line}#{separator}" }
end

#rewindObject

positioning ================


428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/mongo/gridfs/grid_store.rb', line 428

def rewind
  if @curr_chunk.chunk_number != 0
    if @mode[0] == ?w
      delete_chunks
      @curr_chunk = Chunk.new(self, 'n' => 0)
    else
      @curr_chunk == nth_chunk(0)
    end
  end
  @curr_chunk.pos = 0
  @lineno = 0
  @position = 0
end

#seek(pos, whence = IO::SEEK_SET) ⇒ Object



442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/mongo/gridfs/grid_store.rb', line 442

def seek(pos, whence=IO::SEEK_SET)
  target_pos = case whence
               when IO::SEEK_CUR
                 @position + pos
               when IO::SEEK_END
                 @length + pos
               when IO::SEEK_SET
                 pos
               end

  new_chunk_number = (target_pos / @chunk_size).to_i
  if new_chunk_number != @curr_chunk.chunk_number
    @curr_chunk.save if @mode[0] == ?w
    @curr_chunk = nth_chunk(new_chunk_number)
  end
  @position = target_pos
  @curr_chunk.pos = @position % @chunk_size
  0
end

#tellObject



462
463
464
# File 'lib/mongo/gridfs/grid_store.rb', line 462

def tell
  @position
end

#ungetc(byte) ⇒ Object



350
351
352
353
# File 'lib/mongo/gridfs/grid_store.rb', line 350

def ungetc(byte)
  @pushback_byte = byte
  @position -= 1
end

#write(string) ⇒ Object



393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/mongo/gridfs/grid_store.rb', line 393

def write(string)
  raise "#@filename not opened for write" unless @mode[0] == ?w
  # Since Ruby 1.9.1 doesn't necessarily store one character per byte.
  if string.respond_to?(:force_encoding)
    string.force_encoding("binary")
  end
  to_write = string.length
  while (to_write > 0) do
    if @curr_chunk && @curr_chunk.data.position == @chunk_size
      prev_chunk_number = @curr_chunk.chunk_number
      @curr_chunk = GridFS::Chunk.new(self, 'n' => prev_chunk_number + 1)
    end
    chunk_available = @chunk_size - @curr_chunk.data.position
    step_size = (to_write > chunk_available) ? chunk_available : to_write
    @curr_chunk.data.put_array(ByteBuffer.new(string[-to_write,step_size]).to_a)
    to_write -= step_size
    @curr_chunk.save
  end
  string.length - to_write
end