mongodb/mongo-ruby-driver

View on GitHub
docs/reference/gridfs.txt

Summary

Maintainability
Test Coverage
.. _gridfs:

******
GridFS
******

.. default-domain:: mongodb

.. contents:: On this page
   :local:
   :backlinks: none
   :depth: 1
   :class: singlecol

The driver provides a clean and simple interface to work with storage of
chunked files in the database, also known as the pattern "GridFS". The API allows you to either
work with Grid::File objects or with read and write streams.

Creating a GridFS object ("Grid::FSBucket")
===========================================

You can create a GridFS object by calling ``fs`` on a database, with optional
arguments. ``fs`` returns a ``Grid::FSBucket`` object.

The options that ``Grid::FSBucket`` supports are:

.. list-table::
   :header-rows: 1
   :widths: 40 80

   * - Option
     - Description
   * - ``:bucket_name``
     - The name of the GridFS Bucket. Default is ``fs``.
   * - ``:fs_name``
     - The name of the GridFS Bucket. Takes precedence over ``bucket_name``.
       Default is ``fs``.
   * - ``:chunk_size``
     - Specifies the size of each file chunk in the database.
   * - ``:write_concern``
     - The write concern to use when uploading files. Please see the
       :ref:`Write Concern <write-concern>` section under CRUD operations
       for how to work with write concerns.
   * - ``:write``
     - Deprecated. Same as ``:write_concern``.
   * - ``:read``
     - The read preference to use when downloading files.


For example, you can create a GridFS bucket object with a particular read preference:

.. code-block:: ruby

  fs_bucket = database.fs( read: { mode: :secondary } )


Working with write streams
==========================

To upload a file to GridFS using a write stream, you can either open a stream
and write to it directly or write the entire contents of an ``IO`` object to
GridFS all at once.

To open an upload stream and write to it:

.. code-block:: ruby

  File.open('/path/to/my-file.txt', 'r') do |file|
    fs_bucket.open_upload_stream('my-file.txt') do |stream|
      stream.write(file)
    end
  end

To upload the entire contents of an IO object in one call:

.. code-block:: ruby

  File.open('/path/to/my-file.txt', 'r') do |file|
    fs_bucket.upload_from_stream('my-file.txt', file)
  end

Write streams support the following options:

.. list-table::
   :header-rows: 1
   :widths: 40 80

   * - Option
     - Description
   * - ``:chunk_size``
     - Specifies the size of each file chunk in the database.
   * - ``:write_concern``
     - The write concern to use when uploading files. Please see the
       :ref:`Write Concern <write-concern>` section under CRUD operations
       for how to work with write concerns.
   * - ``:write``
     - Deprecated. Same as ``:write_concern``.

The options can be provided as the last argument to the write stream methods:

.. code-block:: ruby

    fs_bucket.open_upload_stream('my-file.txt', write_concern: {w: 2}) do |stream|
      stream.write_concern
      # => #<Mongo::WriteConcern::Acknowledged:0x46980201422160 options={:w=>2}>

      # ...
    end

    fs_bucket.upload_from_stream('my-file.txt', file, write_concern: {w: 2})


Working with read streams
=========================

To download a file from GridFS using a read stream, you can either open a
read stream and read from it directly or download the entire file all at once.

To open a download stream and read from it:

.. code-block:: ruby

  File.open('/path/to/my-output-file.txt', 'w') do |file|
    fs_bucket.open_download_stream(file_id) do |stream|
      file.write(stream.read)
    end
  end

To download the file all at once and write it to an IO object:

.. code-block:: ruby

  File.open('/path/to/my-output-file.txt', 'w') do |file|
    fs_bucket.download_from_stream(file_id, file)
  end

You can also download a file specified by a name and (optionally)
revision number. Revision numbers are used to distinguish between files
sharing the same name, ordered by date of upload. The revision number passed to
``open_download_stream_by_name`` can be positive or negative.

.. code-block:: ruby

  File.open('/path/to/my-output-file.txt', 'w') do |file|
    fs_bucket.open_download_stream_by_name('my-file.txt', revision: -2) do |stream|
      file.write(stream.read)
    end
  end

To download the entire contents of the file specified by name and (optionally)
revision number:

.. code-block:: ruby

  File.open('/path/to/my-output-file.txt', 'w') do |file|
    fs_bucket.download_to_stream_by_name('my-file.txt', file, revision: -2)
  end

Read streams support the following options:

.. list-table::
   :header-rows: 1
   :widths: 40 80

   * - Option
     - Description
   * - ``:read``
     - The read preference to use when downloading files.

Some, but not all, of the read methods listed above pass these options to
the underlying read streams. Please consult the API documentation for each
method to determine whether it supports a particular option.

Finding file metadata
=====================

You can retrieve documents containing metadata about files in the GridFS files collection.

.. code-block:: ruby

  fs_bucket.find(filename: 'my-file.txt')

Deleting files
==============

You can delete a file by id.

.. code-block:: ruby

  fs_bucket.delete(file_id)


Working with Grid::File objects
===============================

This object can be used to wrap a file to be inserted into the database using
GridFS and the object that is retrieved.

To create a file with raw data:

.. code-block:: ruby

  file = Mongo::Grid::File.new('I am a file', :filename => 'new-file.txt')

To create a file from a Ruby ``File`` object:

.. code-block:: ruby

  file = File.open('/path/to/my-file.txt')
  grid_file = Mongo::Grid::File.new(file.read, :filename => File.basename(file.path))

To change file options such as chunk size, pass options to the constructor:

.. code-block:: ruby

  file = File.open('/path/to/my-file.txt')
  grid_file = Mongo::Grid::File.new(
    file.read,
    :filename => File.basename(file.path),
    :chunk_size => 1024
  )

The following is a full list of the available options that files support.

.. list-table::
   :header-rows: 1
   :widths: 40 80

   * - Option
     - Description
   * - ``:chunk_size``
     - Sets the size of each file chunk in the database.
   * - ``:content_type``
     - Set a content type for the file.
   * - ``:filename`` (Required)
     - The file name.
   * - ``:upload_date``
     - The date the file was uploaded (stored).


Inserting Files
===============

Files can be inserted into the database one at a time. File chunks are inserted
by default into the ``fs.chunks`` collection and file metadata is inserted into the
``fs.files`` collection.

.. code-block:: ruby

  client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
  file = Mongo::Grid::File.new('I am a file', :filename => 'new-file.txt')

  client.database.fs.insert_one(file)

To insert into collections with a name prefix other than ``fs``, access the
filesystem with a ``:fs_name`` option.

.. code-block:: ruby

  client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
  file = Mongo::Grid::File.new('I am a file', :filename => 'new-file.txt')

  client.database.fs(:fs_name => 'grid').insert_one(file)

When the driver is inserting the first file into a bucket, it will attempt to create the required
indexes on ``files`` and ``chunks`` collections. The required indexes are as follows:

.. code-block:: ruby

  # files collection
  { :filename => 1, :uploadDate => 1 }

  # chunks collection
  { :files_id => 1, :n => 1 }, { :unique => true }

.. note::

  If the indexes cannot be created, such as due to the current user lacking the permissions to do so,
  the file insert will be aborted. If the application does not have permissions to create indexes,
  a database administrator must create the required indexes ahead of time.

  If the bucket already has files, the driver will not attempt to create indexes, even if they are
  missing and the current user has permissions to create them. In this case a database administrator
  should create the needed indexes as soon as possible to ensure data integrity.

Files can also be streamed as an alternative to a direct insert.

.. code-block:: ruby

  client.database.fs.open_upload_stream(filename) do |stream|
    stream.write(file)
  end

Finding Files
=============

To retrieve a file from the database, call ``find_one`` with the appropriate filter.

.. code-block:: ruby

  client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
  client.database.fs.find_one(:filename => 'new-file.txt') # Returns a Mongo::Grid::File

Files can also be streamed as an alternative to a direct find.

.. code-block:: ruby

  client.database.fs.open_download_stream(file_id) do |stream|
    io.write(stream.read)
  end

  fs.download_to_stream(file_id, io)


Deleting Files
==============

To delete a file, pass the file object to ``delete_one``.

.. code-block:: ruby

  client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
  fs = client.database.fs
  file = fs.find_one(:filename => 'new-file.txt')
  fs.delete_one(file)