Using the Amazon S3 Service and PHP

Follow Me on Pinterest

Amazon Web ServicesRecently I worked on a project that required manipulating and sending images to Amazon S3 (Simple Storage Service). When using this service it is possible to not only send the original image that a user uploads but also to process, resize, crop without the need to do any file writing on your own server.

You can signup for the Amazon S3 service for free but if you are looking for use of the service on a larger scale you will need to set up a payment plan.

In order to use the service we will first need to download the Amazon S3 PHP class which is available on Github, this provides a simple api interface for us to list buckets, transfer files and get our objects. You will also need a PHP server running the GD library.

The first thing we will do is setup a page called config.inc.php and add the following constant values.

//amazon s3 details
define("S3_ACCESS_KEY_ID", "YOUR_ACCESS_KEY");
define("S3_SECRET_ACCESS_KEY", "YOUR_ACCESS_SECRET");
define("S3_URL", "YOUR_S3_URL");

The next step is to setup a class that will handle creating the thumbnail and resized version of our file and also handle transferring the image to our Amazon S3 service. The class will support uploading of JPG, GIF & PNG and convert and transfer our files as JPG’s.

Out AmazonS3Handler Class:

//pull in the s3 api class
require_once("S3.php");

class AmazonS3Handler
{
	/**
	 * PROPERTIES
	*/

	//the s3 object
	private $s3;

	/**
	 * AmazonS3Handler - __construct()
	 *
	 * Construct
	 */

	public function __construct()
	{
		//check to see if the keys have been set
		if (!defined('S3_ACCESS_KEY_ID') && $useAmazonS3Service) throw new MVCException("No S3 access key was defined");
		if (!defined('S3_SECRET_ACCESS_KEY') && $useAmazonS3Service) throw new MVCException("No S3 access secret was defined");

		//we have the access details and user wants to use the s3 service
		//so create the object
		$this->s3 = new S3(S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY);
	}

	/**
	*      AmazonS3Handler - TransferImageToBucket()
	*
	* 	Function to transfer image to the Amazon S3 service
	*
        *	@param	string		$method
	*	@param	object		$file
	*	@param	string		$bucketName - bucket name
	*	@param	string		$filename - name of file to store on s3 service
	*
	*/

	public function TransferImageToBucket($method, $file, $bucketName, $filename)
	{
                //determine the method of transferring the image
		switch($method)
		{
			//user wants to use a file url
			case 'file':

				//pass a file url
				if($this->s3->putObject($this->s3->inputFile($file, false), $bucketName, $filename, S3::ACL_PUBLIC_READ, array(), array("Content-Type" => 'image/jpeg')))
				{
					//return true as file was uploaded
					return true;
				}
				else
				{
					//return false as there was a problem
					return false;
				}

				break;
			//user is passing a string data resource
			case 'string':

				//user a string and upload to s3 service bucket
				if ($this->s3->putObject($file, $bucketName, $filename, S3::ACL_PUBLIC_READ, array(), array("Content-Type" => 'image/jpeg')))
				{
					//return true as file was uploaded
					return true;
				}
				else
				{
					//return false as there was a problem
					return false;
				}
				break;
			//user is passing an input resource
			case 'resource':

				//pass a resource
				if($this->s3->putObject($this->s3->inputResource(fopen($file, 'rb'), filesize($file)), $bucketName, $filename, S3::ACL_PUBLIC_READ, array(), array("Content-Type" => 'image/jpeg')))
				{
					//return true as file was uploaded
					return true;
				}
				else
				{
					//return false as there was a problem
					return false;
				}
				break;

		}

		//return false
		return false;
	}

	/**
	*	AmazonS3Handler - ConvertOriginalImage()
	*
	* 	Converts an image to a jpg from a gif or png
	*
	*	@param	string		$filename
	*
	*/

	public function ConvertOriginalImage($filename)
	{
		//get the image size
                //we do this as rather than a string match on the extension
                //so we can use the temporary file uploaded from the html form
		$size = getimagesize($filename);
		//determine dimensions
		$width = $size[0];
		$height = $size[1];
		//determine the MIME type
		switch($size["mime"])
		{
			case 'image/gif':
				//image is a gif
				$imageData = imagecreatefromgif($filename);
				break;
			case 'image/jpeg':
				//image is a jpeg
				$imageData = imagecreatefromjpeg($filename);
				break;
			case 'image/png':
				//image is a png
				$imageData = imagecreatefrompng($filename);
				break;
		}
		// create a new image
		$destinationImage = imagecreatetruecolor($width, $height);
		// copy resampled
		imagecopyresampled($destinationImage, $imageData, 0, 0, 0, 0, $width, $height, $width, $height);
		//check for a jpeg so we can determine exif information
                //more information on this on my blog post <a href="" target="_blank">about exif orientation fix</a>
		if ($size["mime"] == "image/jpeg")
		{
			//fix photos taken on cameras that have incorrect
			//dimensions
			$exif = exif_read_data($filename);
			//determine orientation
			if (isset($exif['Orientation']))
			{
				//get the orientation
				$ort = $exif['Orientation'];
				//determine what oreientation the image was taken at
				switch($ort)
				{
					case 2:
						// horizontal flip
						$this->ImageFlip($destinationImage);
						break;
					case 3:
						// 180 rotate left
						$destinationImage = imagerotate($destinationImage, 180, -1);
						break;
					case 4:
						 // vertical flip
						$this->ImageFlip($destinationImage);
						break;
					case 5:
						// vertical flip + 90 rotate right
						$this->ImageFlip($destinationImage);
						$destinationImage = imagerotate($destinationImage, -90, -1);
						break;

					case 6:
						// 90 rotate right
						$destinationImage = imagerotate($destinationImage, -90, -1);
						break;
					case 7:
						// horizontal flip + 90 rotate right
						$this->ImageFlip($destinationImage);
						$destinationImage = imagerotate($destinationImage, -90, -1);
						break;
					case 8:
						// 90 rotate left
						$destinationImage = imagerotate($destinationImage, 90, -1);
						break;

				}

			}

		}
	   	//start getting the buffering
	        ob_start();
		//build the jpeg
		imagejpeg($destinationImage);
		//get the file contents
		$fileContents = ob_get_contents();
		//clean the output
		ob_end_clean();
		//return the
		return $fileContents;
	}

	/**
	*	AmazonS3Handler - ResizeToDimension()
	*
	* 	Resizes an image to fit into a specifie dimension
	*
	*	@param	int			$dimension - dimension to fit into
	*	@param	string		$filename - filename of the image (can be a temporary file reference)
	*
	*/

	public function ResizeToDimension($dimension, $filename)
	{
		//get the image size
		$size = getimagesize($filename);
		//determine dimensions
		$width = $size[0];
		$height = $size[1];
		//get the image data
		switch($size["mime"])
		{
			case 'image/gif':
				//image is a gif
				$imageData = imagecreatefromgif($filename);
				break;
			case 'image/jpeg':
				//image is a jpeg
				$imageData = imagecreatefromjpeg($filename);
				break;
			case 'image/png':
				//image is a png
				$imageData = imagecreatefrompng($filename);
				break;
		}
		// find the largest dimension of the image
		// then calculate the resize perc based upon that dimension
		$percentage = ( $width >= $height ) ? 100 / $width * $dimension : 100 / $height * $dimension;
		// define new width / height
		$newWidth = $width / 100 * $percentage;
		$newHeight = $height / 100 * $percentage;
		// create a new image
		$destinationImage = imagecreatetruecolor($newWidth, $newHeight);
		// copy resampled
		imagecopyresampled($destinationImage, $imageData, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
		//check for a jpeg
		if ($size["mime"] == "image/jpeg")
		{
			//fix photos taken on cameras that have incorrect
			//dimensions
			$exif = exif_read_data($filename);
			//determine orientation
			if (isset($exif['Orientation']))
			{
				//get the orientation
				$ort = $exif['Orientation'];
				//determine what oreientation the image was taken at
				switch($ort)
				{
					case 2:
						// horizontal flip
						$this->ImageFlip($destinationImage);
						break;
					case 3:
						// 180 rotate left
						$destinationImage = imagerotate($destinationImage, 180, -1);
						break;
					case 4:
						 // vertical flip
						$this->ImageFlip($destinationImage);
						break;
					case 5:
						// vertical flip + 90 rotate right
						$this->ImageFlip($destinationImage);
						$destinationImage = imagerotate($destinationImage, -90, -1);
						break;

					case 6:
						// 90 rotate right
						$destinationImage = imagerotate($destinationImage, -90, -1);
						break;
					case 7:
						// horizontal flip + 90 rotate right
						$this->ImageFlip($destinationImage);
						$destinationImage = imagerotate($destinationImage, -90, -1);
						break;
					case 8:
						// 90 rotate left
						$destinationImage = imagerotate($destinationImage, 90, -1);
						break;

				}

			}

		}
	   	//start getting the buffering
	       ob_start();
		//build the jpeg
		imagejpeg($destinationImage);
		//get the file contents
		$fileContents = ob_get_contents();
		//clean the output
		ob_end_clean();
		//return the
		return $fileContents;
	}

      /**
	*	AmazonS3Handler - ImageFlip()
	*
	* 	Flips an image based on coordinates and its width and height
	*
	*	@param	string		$image (image to flip)
	*	@param	int			$x
	*	@param	int			$y
	*	@param	int			$width
	*	@param	int			$height
	*
	*/

	public function ImageFlip(&$image, $x = 0, $y = 0, $width = null, $height = null)
	{

	    if ($width  < 1) $width  = imagesx($image);
	    if ($height < 1) $height = imagesy($image);

	    // Truecolor provides better results, if possible.
	    if (function_exists('imageistruecolor') && imageistruecolor($image))
	    {

	        $tmp = imagecreatetruecolor(1, $height);

	    }
	    else
	    {

	        $tmp = imagecreate(1, $height);

	    }

	    $x2 = $x + $width - 1;

	    for ($i = (int)floor(($width - 1) / 2); $i >= 0; $i--)
	    {

	        // Backup right stripe.
	        imagecopy($tmp, $image, 0, 0, $x2 - $i, $y, 1, $height);

	        // Copy left stripe to the right.
	        imagecopy($image, $image, $x2 - $i, $y, $x + $i, $y, 1, $height);

	        // Copy backuped right stripe to the left.
	        imagecopy($image, $tmp, $x + $i,  $y, 0, 0, 1, $height);

	    }

	    imagedestroy($tmp);

	    return true;

	}
}

Most of the comments explain what the code is doing but effectively this code will allow us to get access to our temporary image reference from a posted HTML form, create two different size images and transfer them to the Amazon S3 service without the need for writing any files to our server.

So now we are going to create our html form which will post our image to a PHP script which will then allow us to handle our files.

<form action="transfer-images.php" method="POST" enctype="multipart/form-data">
<input type="file" name="image" />
<input type="submit" />
</form>

The important part in this form is the action which points to our PHP page that will handle the image manipulation and delivery to the Amazon S3 service. Now that we have our AmazonS3Handler class we can now write the code to do this.

//include our configuration file
require("config.inc.php");
//include our Amazon S3 handler class
require("AmazonS3Handler.class.php");
//store our temporary reference to our image
$image = $_FILES['image']['tmp_name'];
//variable to store the max file size (in MB)
//we will use 10MB but you can change this for your own requirements
$maxFileSize = 10;
//first check to make sure a file was posted
if (empty($_FILES['image']['tmp_name']))
{
	echo "An image was not posted to this script ";
	die();
}
else if (round($_FILES['image']['size'] / 1048576, 2) > $maxFileSize)
{
	echo "Your file exceeds the maximum file limit of {$maxFileSize} MB";
	die();
}
else
{
	//now we have an image and it meets our file size requirements
	//setup the Amazon S3 handler class we built
	$AmazonS3Handler = new AmazonS3Handler();
	$bucketName = "my_bucket"; //you will set this bucket up as part of your amazon S3 service
	$filename = "my_file"; //the name and extension your would like the file to be called in your amazon storage.
	//determine success of converting the file to a jpg and transferring it to the Amazon S3 service
	if ($AmazonS3Handler->TransferImageToBucket("string", $AmazonS3Handler->ConvertOriginalImage($_FILES['image']['tmp_name']), $bucketName, $filename . ".jpg")  )
	{
		//This has been successful so we can now create other sizes for our images and transfer them.
		if ($AmazonS3Handler->TransferImageToBucket("string", $AmazonS3Handler->ResizeToDimension(120, $_FILES['image']['tmp_name']), $bucketName, $filename . "_120.jpg") && $AmazonS3Handler->TransferImageToBucket("string", $AmazonS3Handler->ResizeToDimension(400, $_FILES['image']['tmp_name']), $bucketName, $filename . "_400.jpg"))
		{
			echo "Files successfully transferred to Amazon S3";
		}
	}
}

Thats it! No file have had to be written to your server and you files can now be accessed by going to your Amazon S3 url and type the filename and the image should come up in your browser.

Let me know if you found my blog post helpful below.

Filed Under: Blog, Code Share, Media
Tags: , , , , .
Bookmark: permalink.

Let me know what you think:

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>