AWSNinja Code Library
Name: awsninja_cloudfrontimageservice
AWS Services: CloudFront, S3
Source: at GitHub
In a Sentence: A system for optimal Image management and performance in CloudFront.
Not interested in a story? Go Right to the Framework.
You’re the lead developer at a hot new startup called Woofbook. Elevator pitch: “It’s like Facebook meets Flickr, but for dogs.” The founder of Woofbook has started many companies, and run them to many different levels of success and failure, by the shear force of his outgoing personality. In other words, he is a typical web entrepreneur. One day, while getting his goatee manscaped, a barber tells your boss that his nephew told him that Cloud Computing is the next big thing. He rushes back to the office and announces “We need to harness the cloud!” After searching for “Cloud” on TechCrunch, your boss tells you “Cloudfront is the thing we need.”
You take a moment to marvel that a pretty good idea was actually derived from such ridiculous reasoning. You know that using CDN is actually a very good practice and you’re pleased that you will actually spend the next few days working on something that will actually provide long-term value to the project.
It just so happens that you were building the photo-sharing section of Woofbook when your boss interrupted you with his Cloudfront idea. After 15 minutes of cruising the Amazon’s Cloudfront website and a few blog posts, you conclude that implementing Cloudfront is going to be easier than you thought. You just need to create a Cloudfront distribution, upload your images to it, then point the HTML image tags to the Cloudfront Service. Perfect.
A week later, the photo-sharing feature goes live and it’s a big hit. Before long users have uploaded over 100,000 photos and it loads pretty fast and you think you’re pretty darn sophisticated for making it look so easy.
Several weeks after that, your boss comes back from a meeting with a potential investor (those meetings always tend to result in bothersome new initiatives) and exclaims, “The photo sharing is great! We need to leverage it.”
It turns out that dogs have active social lives. They go out to dog gatherings every day at specially designated meeting places (known as dog parks) to socialize and conduct their. . . ahem – business.
“It would be great if dogs could tag their friends in the photos they uploaded.” your boss says. “Also, wouldn’t it be great if one dog could use a photo uploaded by another dog as his profile pic?”
“No problem!” you say confidently. You smartly designed the database architecture to make it easy to use the photos on the site for virtually any purpose. “I’ll have it done by COB tomorrow.” You link up the Photos table to the Users table by adding a “profilePhotoId” foreign-key field to the Users table. Then you build out the functionality to allow users to choose any photo as their profile pic.
But you soon realize there is a problem. Profile pics are displayed at 200×150 pixels and the photo album photos typically display at 500×375. You’re afraid you don’t have time to properly resize the photos without missing your self-imposed deadline. To speed things up you try resizing the actual photo images by using the WIDTH and HEIGHT attributes of the IMG tag, but that causes the images to appear warped because the aspect-ratios are different.
So now you need to run a script to resize the photos. Or maybe you’ll just resize the subset of the album photos that users have chosen as profile pics. You realize there’s a trade-off between these two options. It’s more logically simple to store all of the photos in two sizes, but it’s more efficient and elegant to only store the profile-sized images for images that will actually be used as profile pics. Unfortunately, you promised it would be done “by COB tomorrow.” To meet your deadline you settle on resizing all of the photos and uploading them all on Cloudfront. Even though it is less efficient to do it this way, it also involves less programming which means you’ll meet your deadline. You run a process to resize the photos overnight and finish up the next day. Everybody’s happy.
Several months later, things are continuing to go well at Woofbook Inc. Users are loving the ability to use their friends’ photos as their profile pics. Woofbook is starting to get positive coverage on TechCrunch and even the regular business press is beginning to take notice. Imitators start to appear on the scene. One of them, BarkSquare, decides to capitalize on some bad press Woofbook received about some ill-advised privacy-policy changes to promote their new “Transfer to BarkSquare” feature. The feature allows users to copy their entire Woofbook profile, including all of their friends and photos, to BarkSquare.
Predictably, this causes a significant panic in the WoofBook offices. “How will we stop this?” your boss cries with the agony not unlike the pleadings of a bullied child. After some determined and serious discussion, he decides that all of the photos should be watermarked. “At least we’ll be able to keep track of where the images go and get some free advertising,” he says.
You have your doubts. First of all, it was the VP of Marketing who implemented the privacy policy change. You didn’t create this problem, but it belongs to you now. Also, you think that making it harder for users to switch from Woofbook to others might further erode users’ trust and exacerbate the problem. Unfortunatly, you’re just a lowly programmer and your opinion isn’t really valued as much as your boss would like you to believe.
You roll your eyes and get to work figuring out how to add watermarks to the images. Once you’ve created and tested the script that will apply the watermarks, you kick it off and leave it running as you head home for the evening. Before going to bed, you check the script’s progress, see that it’s finished, and send a quick email to your boss to let him know. You’re quite pleased that the timestamp on your email says 11:32pm. You expect that your dedication will not go unnoticed.
When you arrive at work the next morning, rather than thanking you for your after-hours efforts, your boss wants to know why he’s not seeing any watermarks on the photos on the site. That’s when you remember. Because of the chaos and urgency in the office yesterday, you forgot something important. The images on Cloudfront are cached on the edge locations. Updating the original does not automatically update the cached versions residing around the world. Worse, there is no way to manually flush the cache. You can only wait for the Cloudfront system to refresh the cache on it’s own. Since you followed the best-practice of setting a far future Expires header on the images, it can potentially be a very long time before the cached objects get refreshed.
Now it’s even more complicated. In order to show the new images with the watermarks, you have to upload all of them with different file names, then point to the new versions. It’s going to be a long day.
The CloudfrontImageService is designed to eliminate the pain of managing images on Cloudfront distributions. The framework has built-in functionality for managing:
There are three database tables that are used to manage images in the CloudFrontImageService. They are tbl_image, tbl_imageDimensions, and tbl_imageDimensionsMap.

tbl_images – This table is used to track the original image file that is stored on your server’s filesystem or on an EBS volume (not on S3).
tbl_dimensions – This table holds the definitions of different dimension sizes like: thumbnail, x-large, original and whatever else you need.
tbl_imageDimensionsMap – This table holds the records of actual image objects that are currently residing in in your Cloudfront distribution.
Before you can serve up images in your webpages, you need to add them to the framework. This is done through the createImageObjectFromFileSystemPath() method. Example:
<?php
$cfImgSvc = new CloudfrontImageService();
$imgObj = $cfImgSvc->createImageObjectFromFileSystemPath('/var/www/imagedrop/flower.jpg', 'flowers/flower.jpg');
?>
On line two we instantiate the CloudFrontImageService. On line four we use the createImageObjectFromFileSystemPath() method to take an image saved on the filesystem at location /var/www/imagedrop/flower.jpg and create an Image object which will be stored in the flowers subdirectory of the IMAGES_ROOT that is set in the config file. The createImageObjectFromFileSystemPath() handles the following steps:
Once you have added an image to the framework, you can use the CloudfrontImageService class to insert the image URL into your HTML. CloudfrontImageService has three methods that you can use to do this:
getUrlFromFilePath($filePath, $dimensionKeyName) – Takes the $filePath (which is one found in tbl_images) and a valid dimensionKey (which is a keyName found in tbl_imageDimensions). It returns a URL to the image on Cloudfront. If the image doesn’t exist on Cloudfront, it automatically uploads it to your Cloudfront distribution it before returning the URL.
<html>
<body>
<img src="<?php
echo $cfImgSvc->getUrlFromFilePath('flowers/flower.jpg', 'thumbnail');
?>"/>
</body>
</html>
This will insert the URL of the thumbnail-sized image into the HTML output. The thumbnail dimensionkey (thumbnail) must be defined in tbl_imageDimensions. The resulting URL will look something like this:
http://cdn.docmonk.com/flowers/flower_1_thumbnail_136x111.jpg
The version, dimensionkey, width, and height are all part of the URL for the purposes of ensuring that it is a unique URL and for your convenience.
getUrlFromImageId($imageId , $dimensionKeyName) – Same as getUrlFromFilePath(), except that you provide the Image id.
<html>
<body>
<img src="<?php
echo $cfImgSvc->getUrlFromImageId(55, 'extra-large');
?>"/>
</body>
</html>
getUrl(Image $imgObj, $dimensionsKeyName) – Same as the two above, except you can use it if you already have the Image object.
<html>
<body>
<img src="<?php
$imgObj = Image::findById(55);
echo $cfImgSvc->getUrl($imgObj, 'original');
?>"/>
</body>
</html>
Get the AWS Ninja CloudFrontImageService from GitHub. You will also need the AWS Ninja Core. Installation instructions are the in the package README.txt files.
Josh Smith
August 5th, 2010 at 6:51 pm
Hi, Jay. Great framework you’ve posted here and saved me many hours of otherwise unnecessary work.
Was wondering what you think of these additions I made to your framework for my own purposes. I make a lot of thumbnails 50×50 px, so here’s what I added to
getNewDimensionsPreservingAspectRatio():if ($targetX == $targetY) { // Used for thumbnails, e.g. 50x50px $this->thumb = 'yes'; // Set the thumb to yes so createAndUploadImageDimensionMap() will know to process it differently // Preserve X==Y aspect ratio $res['x'] = $targetX; $res['y'] = $targetY; }And then to
createAndUploadImageDimensionMap():// Check to see if thumbnail or not so we can crop it if($this->thumb == 'yes') { // If the width is larger, we need to offset the width for cropping if ($width > $height) { // We should look for the image's center $x_offset = intval(($width - $height)/2); MagickCropImage($magick_wand, $height, $height, $x_offset, 0); } // If the height is larger, we need to offset the height for cropping else { // We should look for the image's center $y_offset = intval(($height - $width)/2); MagickCropImage($magick_wand, $width, $width, 0, $y_offset); } }I’m sure there’s room for improvement here, so would love to know what you think. Thanks a bunch for writing this up, though. Really appreciate it.
Jay Muntz
August 6th, 2010 at 9:45 am
Josh – I’m so glad you found it useful. I guess your project had the requirement to crop the photos and make 50×50 square images, and it looks like you were able to modify the code to meet that requirement. That exactly how I hoped people would use it. Thanks!
Josh Smith
September 2nd, 2010 at 6:09 pm
For anyone using my code above, note that after calling
MagickCropImage($magick_wand, $height, $height, $x_offset, 0);above, you should also callMagickResetImagePage($magick_wand, NULL);. While I’m not entirely sure why this is strictly necessary (especially since we’re only calling a single crop on the image), it makes it work where things might otherwise break.If you really want to get bored to tears, this documentation explains the ins and outs: http://www.imagemagick.org/Usage/crop/#crop_repage
A PHP Framework for Hosting your JavaScript Applications on CloudFront « AWS Ninja
September 28th, 2010 at 12:16 am
[...] previously written about managing your site’s images on CloudFront, and this article will basically tackle the same [...]
Chris
June 1st, 2011 at 7:59 am
Hi Jay, I have two questions..
1) How come you decided to use MagickWand? What is it that MagickWand uses that ImageMagick cannot? I can’t get MagickWand installed on PHP5.3.x that is all. I’m going to do a full port of your framework so that I can use it natively in my application with Doctrine + ZF.
2) When you use the echo $cfImgSvc->getUrlFromFilePath(‘flowers/flower.jpg’, ‘thumbnail’);
You mention this is your “just in time” file uploads. Can I ask a little more information about this? Where are the files uploaded to? Your server to S3, which is then “propogated” automatically to your CloudFront distribution? Or do you miss out S3 entirely?
3) On a similar note.., it appears as though this feature would cause massive page load delays. Considering PHP can’t support async/multiple uploads through threads, lets say 4 images are required on a page, the first time it is loaded… how does this work?
- Each image is 250kb in size. Requiring 1mb in total transfer.
So your server which stores a local copy of the files then transfers them to S3, then CloudFront automatically receives them? This must really slow down things?
4) Do you still have to store a local version of each file? I was thinking about just only using S3 RRS. I don’t particularly care about each file. Loosing one is not an issue, but I am in *constant* need of creating new versions of the files. I need to constantly create new watermarks of each image since we are providing these images to resellers who require their own watermark.
Geoffery Miller
July 23rd, 2011 at 10:33 am
I impressed by this work. I am considering making use of it for my own web application that I will host on Amazon. Thanks for sharing!
Thank you for puting the time in to publish this info. I found it very useful. If you are ever interested in deep linking directories then please contact me.
December 2nd, 2011 at 6:14 pm
Thank you for puting the time in to publish this info. I found it very useful. If you are ever interested in deep linking directories then please contact me….
[...]Heya i am for the primary time here. I found this board and I find It really useful & it helped me out much. I am hoping to present one thing again and aid others such as you helped me.[...]…
TopBrandsS
December 28th, 2011 at 3:22 pm
Nulled Scripts – Ace Brands 2k12 – best software…
During my research about 64 and 32 tittle operating method I initiate numberless people confused close to what computer they should purchase or what bit approach they should prefer…I invent this article should be a great serve in behalf of people to pick out the apt party in place of their use.
nulled scripts
Vicente Lucatero
December 28th, 2011 at 6:51 pm
Hey there, You have done an excellent job. I will definitely digg it and personally suggest to my friends. I’m sure they will be benefited from this site.
online deals
December 28th, 2011 at 7:07 pm
online deals…
[...]hello there and thanks on your information ? I’ve definitely picked up anything new from proper here. I did alternatively experience some technical issues the use of this web site, as I skilled to reload the web site a lot of occasions previous t…
disney cruise line
December 28th, 2011 at 10:15 pm
21…
[…]We never really grow up, we only learn how to act in public. […]…
Dich vu chat luong hang dau
December 29th, 2011 at 12:57 am
Dich vu chat luong hang dau…
[...]I was recommended this website by means of my cousin. I am no longer certain whether or not this post is written by him as nobody else understand such unique approximately my problem. You’re incredible! Thank you![...]…
films streaming
December 29th, 2011 at 4:10 am
films streaming…
[...]hollywood movies, fantastic films, no ????[...]…
nerds on site
March 4th, 2012 at 2:05 pm
nerds on site…
[...]You really make it appear really easy along with your presentation but I to find this matter to be actually one thing which I think I would by no means understand. It kind of feels too complicated and very broad for me. I’m looking forward for yo…
Hosting
March 26th, 2012 at 4:02 am
Hosting…
[...]I delight in, result in I discovered exactly what I was having a look for. You’ve ended my four day long hunt! God Bless you man. Have a great day. Bye[...]…
my website
May 4th, 2012 at 6:18 pm
my website…
[...]Wow, amazing blog structure! How long have you ever been running a blog for? you made blogging look easy. The total glance of your site is great, let alone the content material![...]…