Skip to content

3. GridFS File Storage

Jon Clausen edited this page Jan 2, 2016 · 19 revisions

CBMongoDB includes an interface and Active Entity implementation for using MongoDB's GridFS for relational, distributed file storage. Let's say, for example, that we want to associate profile images to our people collection.

Let's create a relational entity that will be responsible for storing and retrieving profile pictures. Our component should extend cbmongodb.models.FileEntity. An additional component attribute may be specified with the name of the bucket used to store the file data. By default, Mongo uses a bucket named fs.

component name="ProfileImages" extends="cbmongodb.models.FileEntity" collection="profileImages" bucket="images" accessors=true{
	//normalize our person information, in case we need it for captions and alt information.
	//Note that we are using auto-normalization of the first_name and last_name keys on our person entity
	property name="person" schema=true normalize="Person" on="person.id" keys="first_name,last_name";
		property name="person.id" schema=true required=true;	
}

Because the querying syntax for GridFS files differs from our standard query syntax, entities use a standard MongoDB collection which connects to the GridFS file object. Every FileEntity schema contains a property fileId which allows for a one-to-one relationship to the GridFS file.

We can load a file in directly from a local path, for example a file uploaded to a temporary directory:

var Person = getModel("Person").load("54e523599b67bde3c7b2f03d");

var ProfilePhoto = getModel("ProfileImages")
//Set and normalize our person model
ProfilePhoto.set('person.id',Person.get_id())
//Load our photo in to grid FS and pass the optional argument to delete the file after it has been loaded
ProfilePhoto.loadFile(filePath=expandPath('/includes/tmp/MyProfilePhoto.jpg'),deleteFile=true);
ProfilePhoto.create();

Working With Images and Files

Once we've created our profile photo, we can retrieve using our person relationship:

var ProfilePhoto = getModel("ProfileImages").where('person.id',Person.get_id()).find();

A simple example would be to retrieve the file and write it back out to the file system. The writeTo returns the written file path method can accept either a full path or a directory. If only a directory is specified, the file will be written with the fileId as the name of the file.

var imageFile = ProfilePhoto.writeTo(expandPath('/assets/images/profiles/MyProfileImage.jpg'));

For images, we can also provide a struct of arguments to resize or crop the image before writing it out to the file system.

  • width: The maximum width of the image. If crop x and y coordinates are specified, this will be the actual width. Otherwise, if only height and width are provided, the image will be scaled to the maximum height/width constraints.
  • height: Same as width, but for height
  • x: The horizontal center of the cropped image from the top left corner of the original image
  • y: The vertical center of the cropped image bounding box

Write a scaled image which will be no larger than 100x100 pixels wide:

var imageFile = ProfilePhoto.writeTo(
	Path=expandPath('/assets/images/profiles/MyProfileImage.jpg'),
	imageArgs={"height":100,"width":100}
);

Non-image files may also be written to the file system with the writeTo(Path) method, though image arguments will be ignored.

Additional Image Methods

CBMongoDB is bundled with the JavaXT Image library, which allows for advanced image manipulation. We can retrieve the modifiable JavaXT image object through the getImageObject() method. The example below retrieves the image, crops the image to 100x100 pixels from the center of the image and writes it out to a file:

var imgObject = ProfilePhoto.getImageObject();
var width = imgObject.getWidth();
var height = imgObject.getHeight();
imgObject.crop(width/2,height/2,100,100);
imgObject.saveAs(expandPath('/assets/images/profiles/MyProfileImage.jpg'));