Storing Files with MongoDB GridFS post

Posted on 2010-07-27 by jwage


The PHP MongoDB extension provides a nice and convenient way to store files in chunks of data with the MongoDB GridFS. It uses two database collections, one to store the metadata for the file, and another to store the contents of the file. The contents are stored in chunks to avoid going over the maximum allowed size of a MongoDB document.

You can easily setup a Document that is stored using the MongoDB GridFS by using the @File annotation:

namespace Documents;

/** @Document(collection="files") */
class Image
{
    /** @Id */
    private $id;

    /** @String */
    private $name;

    /** @File */
    private $file;

    private function getId()
    {
        return $this->id;
    }

    private function setName($name)
    {
        $this->name = $name;
    }

    private function getName()
    {
        return $this->name;
    }

    private function getFile()
    {
        return $this->file;
    }

    private function setFile($file)
    {
        $this->file = $file;
    }
}

Notice the $file property with @File annotation, it tells the Document that it is is to be stored using the MongoGridFS and the MongoGridFSFile instance is placed in the $file property for you.

Now you can create a new Image setting the path to a file and persist it:

$image = new Image();
$image->setName('Test image');
$image->setFile('/path/to/image.png');

$dm->persist($image);
$dm->flush();

Then later you can retrieve that image and render it:

$image = $dm->createQuery('Documents\Image')
    ->field('name')
    ->equals('Test image')
    ->getSingleResult();

header('Content-type: image/png;');
echo $image->getFile()->getBytes();

You can of course make references to this Image document from another document. Imagine you had a Profile document and you wanted every Profile to have a profile image:

namespace Documents;

/** @Document(collection="profiles") */
class Profile
{
    /** @Id */
    private $id;

    /** @String */
    private $name;

    /** @ReferenceOne(targetDocument="Documents\Image") */
    private $image;

    private function getId()
    {
      return $this->id;
    }

    private function getName()
    {
        return $this->name;
    }

    private function setName($name)
    {
        $this->name = $name;
    }

    private function getImage()
    {
        return $this->image;
    }

    private function setImage(Image $image)
    {
        $this->image = $image;
    }
}

Now you can create a new Profile and give it an Image:

$image = new Image();
$image->setName('Test image');
$image->setFile('/path/to/image.png');

$profile = new Profile();
$profile->setName('Jonathan H. Wage');
$profile->setImage($image);

$dm->persist($profile);
$dm->flush();

If you want to query for the Profile and load the Image reference in a query you can use:

$profile = $dm->createQuery('Profile')
    ->field('name')->equals('Jonathan H. Wage')
    ->getSingleResult();

$image = $profile->getImage();

header('Content-type: image/png;');
echo $image->getFile()->getBytes();

Categories: articles