Zend Framework: Zend_Service_Amazon_S3

Introduction

From: http://www.phpriot.com/articles/zend-service-amazon-s3

In this article I will introduce you to the Zend_Service_Amazon_S3 component of the Zend Framework. This component makes use of Amazon Web Services (AWS) to manage files on Amazon’s Simple Storage Service (S3).

Firstly, I will tell you how to sign up with AWS, then I will introduce you to buckets and show you how to manage them. Next, I will show you how send files to your Amazon S3 bucket so you can use it as a Content Delivery Network (CDN) for your web site.

This article requires Zend Framework, downloadable from http://framework.zend.com. At time of writing, the current version of Zend Framework is 1.10.2.

I have written this article to share my own experiences implementing Amazon S3 into Recite CMS, my company’s PHP 5 content management system. Recite CMS offers an “asset mirroring” add-on module which allows you automatically mirror files uploaded via the CMS. This is especially useful in conjunction with the web services functionality of Recite CMS.

<!–nextpage–>

Signing Up For Amazon S3

Before you can use Amazon S3, you must create an Amazon AWS account. Visit the following URL and click the Sign Up For Amazon S3 button.

If you don’t already have an AWS account you will now be given the option to create an account. If you already have an AWS account you will be able to log in so S3 access can be given to your account.

Note: Amazon S3 is not a free service. It is however a relatively inexpensive and simple way to create a Content Delivery Network. Pricing for the service is listed at the URL above.

Once you have access to Amazon S3, you must create a pair of keys in order to access the web service. These are referred to as the AWS Key and the AWS Secret Key. Both of these are required.

Managing Your S3 Data

While developing your code that communicates with Amazon S3, you may find it useful to use an S3 client.

If you’re using Mac OS X you can use Cyberduck to connect to S3 and manage your data. On Windows you can use CloudBerry S3 Explorer.

You will need your AWS keys to use these programs.

<!–nextpage–>

Connecting to Amazon S3

Now that you have an AWS Key and an AWS Secret Key you can connect to Amazon S3. To access Amazon S3 with the Zend Framework you will be using the class Zend_Service_Amazon_S3.

For the purposes of this article we’ll assume your AWS Key is stored in a variable called $awsKey, and your AWS Secret Key is stored in a variable called $awsSecretKey. The following example shows how to specify your keys each time you instantiate the class.

Listing 1 Instantiating Zend_Service_Amazon_S3 with keys (listing-1.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);
?>
Listing 2 Specifying AWS keys ahead of time (listing-2.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    Zend_Service_Amazon_S3::setKeys($awsKey, $awsSecretKey);

    $s3 = new Zend_Service_Amazon_S3();
    $anotherS3 = new Zend_Service_Amazon_S3();
?>

<!--nextpage-->

Amazon S3 Buckets

All data stored in Amazon S3 is stored in buckets. A bucket is a collection of data – think of each bucket as a separate drive on your computer.

Before you can store any data on Amazon S3, you must create a bucket. Each account can have up to 100 buckets. Once the bucket has been created, all subsequent operations (such as reading, writing or deleting files) occur on a single bucket.

Caution: Buckets share a common namespace with all users of Amazon S3. For example, if you try to create a bucket called test, you probably won’t be able to since somebody else has probably already taken that name. If you are going to automate creation of buckets, you should use a consistent naming scheme, and should also handle failures should the requested name not be available. If you’re going to store files for a specific domain name, perhaps the name of the bucket could be the same as the domain name.

Every file you store in an Amazon S3 bucket is called an object. Every object has a unique key, which is the value you use to reference the object (e.g. to download or delete the file). You can use any naming structure you like, but if you want to store files in a hierarchical manner (such as files and folders), you can name your objects accordingly.

For instance, if you want to store a file called logo.png in a directory called images, you might give that object a key of images/logo.png.

<!--nextpage-->

Managing Buckets

The operations you can perform on a bucket are as follows:

  • Retrieve a list of buckets
  • Create a new bucket
  • Clear all objects from a bucket
  • Remove a bucket

There are strict limitations on the name of buckets. The names can not be more than 255 characters, and contain only lower-case letters, numbers, underscores, periods and dashes.

Bucket names are used to create a unique domain name by which you can access objects in the bucket. Accessing your files is covered later in this article.

Caution: Bucket names are publicly visible in any URL you use to link to files in your buckets. As such, ensure the names are appropriate for their purpose.

Once you have created a bucket, all subsequent operations on the bucket (including managing objects in the bucket) require the bucket name.

Retrieve a List of Buckets

You can retrieve a list of existing buckets using the getBuckets() method. This returns an array, with each entry being the name of the bucket. The following code demonstrates this.

Listing 3 Retrieving a list of buckets (list-buckets.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);

    var_dump($s3->getBuckets());
?>

Output:

array
  0 => string 'phpriot-test-bucket' (length=19)

<!--nextpage-->

Create a Bucket

Buckets are created using the createBucket() method. This method accepts the name of the bucket as its first argument. If the bucket is successfully created, this method returns true. If the name of the bucket is not valid, an exception is thrown.

If the bucket cannot be created (likely due to somebody else owning that bucket name), false is returned from createBucket().

It is important to note that if you have already created a bucket then try to create it again, true will still be returned. If you want to ensure that a new bucket is being creating you should first retrieve a list of buckets and check for the name.

The following listing demonstrates creating a bucket.

Listing 4 Creating a new bucket (create-bucket.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);

    $bucketName = 'phpriot-test-bucket';

    try {
        $buckets    = $s3->getBuckets();

        if (in_array($bucketName, $buckets)) {
            throw new Exception('You already have this bucket');
        }

        $succ = $s3->createBucket($bucketName);

        if ($succ) {
            echo "Bucket created!";
        }
        else {
            echo "Unable to create bucket!";
        }
    }
    catch (Exception $ex) {
        echo $ex->getMessage();
    }
?>

This code begins by specifying the name of the bucket to create in $bucketName. It then retrieves a list of buckets and checks that you don’t already have that bucket. If so, it throws an exception which is then handled at the bottom of the script.

Next it tries to create the bucket. If the name is invalid an exception is thrown. Otherwise, the call to createBucket() returns a boolean.

<!–nextpage–>

Clearing and Removing Buckets

Clearing a bucket involves removing all objects that are stored within that bucket. This is achieved using the cleanBucket() method. This method accepts as its only argument the name of the bucket.

Buckets are removed using the removeBucket() method. This also accepts as its only argument the name of the bucket. You cannot delete a bucket while there are still objects in it, therefore, if you want to remove a bucket you should first clear it.

The following list demonstrates clearing then removing a bucket.

Listing 5 Clearing and removing a bucket (remove-bucket.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);

    $bucketName = 'phpriot-test-bucket';

    $s3->cleanBucket($bucketName);
    $s3->removeBucket($bucketName);
?>

<!--nextpage-->

Managing Objects in Amazon S3 Buckets

You can manage objects in an S3 bucket either by using the functions provided in the Zend_Service_Amazon_S3 class, or by registering S3 as a PHP stream and using the native stream handling functions (such as fopen(), fread(), fwrite(), etc.).

We’re only going to look at the API methods in this article; refer to Zend Framework documentation for more details about using streams. The primary methods are as follows:

  • putObject() – Used to create a new object with the given data
  • putFile() – Used to create a new object from the given path name
  • putFileStream() – Used to create a new object from a file stream (saves PHP reading in the entire file into memory as putFile() does)
  • removeObject() – Removes the object from the bucket
  • getInfo() – Retrieves information about a given object
  • IsObjectAvailable() – Check if an object exists.
  • getObject() – Retrieves the data for the given object
  • getObjectStream() – Returns the data as a stream (saves PHP reading in the entire object into memory as getObject() does)
  • getObjectsByBucket() – Retrieve a list of objects in a bucket. You can filter this list by a given prefix.

<!–nextpage–>

Creating, Modifying and Removing Objects

There are three ways to send an object to your S3 bucket: using putObject(), putFile() and putFileStream(). I’ll demonstrate each of these shortly.

The first thing to know about objects though is that you can set certain attributes about each object, such as its availability to other uses. By default, all objects are private, meaning only you can read them via the API.

Since we’re setting up a basic content delivery network, we’re going to make all objects available for reading only. To achieve this, we set the S3_ACL_HEADER parameter to S3_ACL_PUBLIC_READ. The default setting is S3_ACL_PRIVATE. You can also manually set the content type of the uploaded data, but if you don’t it will be automatically guessed (we’ll let it guess in these examples).

To begin with, let’s look at putObject(). This method takes three arguments: the object name, the object data, and the parameters. The bucket name is specified as the beginning of the object name. So if you wanted to create an object called my-test.txt (and using our sample bucket name of phpriot-test-bucket), the full object name is phpriot-test-bucket/my-test.txt.

The following listing demonstrates this. It calls putObject() with some arbitrary data and writes it to the object my-test.txt. If writing the data succeeds then true is returned from putObject(), otherwise false is returned.

Listing 6 Writing data to an object using putObject (put-object.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);

    $bucketName = 'phpriot-test-bucket';
    $perms      = array(
        Zend_Service_Amazon_S3::S3_ACL_HEADER =>
            Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ
    );

    $ret = $s3->putObject(
        $bucketName . '/my-file.txt',
        'Some test data',
        $perms
    );

    echo "Success: " . ($ret ? 'Yes' : 'No');
?>

Similar to putObject(), we can send a file using the putFile() method. Instead of specifying data (such as from a PHP variable), we pass a filesystem path instead. Note however the arguments are swapped around from putObject(). That is, the local path is the first argument and the object name is the second argument.

If the local file cannot be found or read then an exception is thrown. The following listing demonstrates this functionality. It assumes we have a file called my-local-file.txt. We’ll upload it to the bucket with the same name.

Listing 7 Writing a local file to an object using putFile (put-file.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);

    $bucketName = 'phpriot-test-bucket';
    $filename   = 'my-local-file.txt';
    $perms      = array(
        Zend_Service_Amazon_S3::S3_ACL_HEADER =>
            Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ
    );

    $ret = $s3->putFile(
        $filename,
        $bucketName . '/' . $filename,
        $perms
    );

    echo "Success: " . ($ret ? 'Yes' : 'No');
?>

Finally, you can use the putFileStream() method to upload a local file. This is a better solution than using putFile() since the file isn’t read into memory before being sent.

Once again you can pass the local path name, but instead of calling putFile() simply call putFileStream().

Modifying an Existing Object

To modify an object you can simply call one of these methods with the same object name and the old data will be replaced.

Removing an Object

You can remove an object from a bucket using the removeObject() method. This method accepts as its only argument the name of the object (including the name of the bucket as the start).

<!–nextpage–>

Getting Information About Objects

To determine if a particular object exists in a bucket, use the isObjectAvailable() method. This method accepts as its only argument the object name (including the bucket name at the start).

You can also get information about a particular object using the getInfo() method. This also accepts the name of the object as its only argument. An array will be returned if the object exists, otherwise false is returned.

The following listing demonstrates the getInfo() method. In this listing we use getInfo() to find out more about the my-file.txt object we created earlier in this article.

Listing 8 Getting meta information about an object (get-info.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $bucketName = 'phpriot-test-bucket';

    $info = $s3->getInfo($bucketName . '/my-file.txt');

    if (!is_array($info)) {
        throw new Exception('Unable to retrieve info!');
    }

    var_dump($info);
?>

Output:

array
  'type' => string 'text/plain' (length=10)
  'size' => string '9' (length=1)
  'mtime' => int 1267746267
  'etag' => string '"bb6bc982bb3c1eedf3515edd9f802161"' (length=34)

<!–nextpage–>

Listing Objects

You can retrieve a list of objects in your bucket using the getObjectsByBucket() method. This method accepts as its first argument the name of the bucket and optionally accepts an array of parameters as its second argument.

The parameters allow you to paginate results, but for now we’ll just look at the prefix parameter. Specifying this parameter allows you to retrieve all objects that begin with a certain name. If you look at objects having a filesystem structure (that is, organised into folders and files), you could retrieve every single file within a given directory.

The following listing demonstrates how to get files within a particular directory. We’ll assume there’s a “directory” in our bucket with the name myDir and that there are a number of files and sub-directories in there.

Listing 9 Getting a list of objects (get-objects-prefix.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);

    $bucketName = 'phpriot-test-bucket';

    $ret = $s3->getObjectsByBucket($bucketName, array(
        'prefix' => 'myDir/'
    ));

    var_dump($ret);
?>

Output:

array
  0 => string 'myDir/file1.txt' (length=15)
  1 => string 'myDir/file2.txt' (length=15)
  2 => string 'myDir/subDir/file3.txt' (length=22)

This listing retrieves every single object with the given prefix. On the other hand, if you only want to retrieve objects that aren’t in a sub-directory (so based on the output of the previous listing, not including the subDir directory), you can also specify the delimiter parameter.

This works by excluding objects in which the specified delimiter string appears. The following listing demonstrates listing only files in the specified directory.

Listing 10 Getting a list of objects in a single directory (get-objects-delimiter.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);

    $bucketName = 'phpriot-test-bucket';

    $ret = $s3->getObjectsByBucket($bucketName, array(
        'prefix'    => 'myDir/',
        'delimiter' => '/'
    ));

    var_dump($ret);
?>

Output:

array
  0 => string 'myDir/file1.txt' (length=15)
  1 => string 'myDir/file2.txt' (length=15)

<!–nextpage–>

Reading Objects

The methods used to retrieve objects from a bucket are getObject() and getObjectStream(). You can use getObject() to return the object data into a PHP variable, while getObjectStream() is most useful for writing an object directly to your local filesystem.

For now, we’ll just look getObject(). This method accepts as its only argument the name of the object, and returns the data found in the object. If the object can’t be returned then false is returned instead.

The following listing demonstrates combining a call to getInfo() and getObject() so you can send the file back to the end-user. It retrieves the information about the file first so it can determine the mime type and file size.

Listing 11 Sending an object directly from an S3 bucket (get-object.php)
<?php
    require_once('Zend/Service/Amazon/S3.php');

    $awsKey       = '[your key]';
    $awsSecretKey = '[your secret key]';

    $s3 = new Zend_Service_Amazon_S3($awsKey, $awsSecretKey);

    $bucketName = 'phpriot-test-bucket';
    $objectName = $bucketName . '/my-file.txt';

    $info = $s3->getInfo($objectName);

    if (is_array($info)) {
        header('Content-type: ' . $info['type']);
        header('Content-length: ' . $info['size']);

        echo $s3->getObject($objectName);
    }
    else {
        header('HTTP/1.0 404 Not Found')
    }
?>

This example is really just for demonstration purposes only. If you were using Amazon S3 as a content delivery network it would be completely redundant (not to mention much slower) to read in the files and send to users as requested.

<!–nextpage–>

Using Objects Stored in Amazon S3 Buckets

Finally, let’s look at how other users can access files you store in your Amazon S3 buckets. The name of the bucket maps directly to the following domain name to the following web addresses:

Using our previous examples from this article, you can access the my-file.txt file at the following URLs:

If we didn’t set the permissions to be publicly readable, then it would not be possible for others to access your files.

You can now link to these files directly from your web pages, rather than hosting your files yourself. The following listing demonstrates how you might achieve this. It uses Amazon S3 for CSS, images, and for a file download link.

Listing 12 Using your Amazon S3 bucket on your web site (use-cdn.php)
<?php
    $bucketName = 'my-company-bucket';
    $cdnUrl = 'http://' . $bucketName . '.s3.amazonaws.com';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <title>Sample Amazon S3 CDN Usage Page</title>
        <link rel="stylesheet"
              href="<?php echo $cdnUrl ?>/css/styles.css"
              type="text/css"
              media="all" />
    </head>
    <body>
        <div>
            <img src="<?php echo $cdnUrl ?>/images/logo.png" alt="My Logo" />
        </div>
        <div>
            <a href="<?php echo $cdnUrl ?>/assets/profile.pdf">
                Download company profile
            </a>
        </div>
    </body>
</html>

<!–nextpage–>

Summary

In this article I introduced you to the Zend_Service_Amazon_S3 component of the Zend Framework, used for managing data on the Amazon S3 (Simple Storage Service) component of Amazon Web Services (AWS).

First I showed you how to access AWS. Next I introduced you to buckets and how to manage them. Next I showed you how to manage data in your buckets. Finally, I showed you how to use the data you’ve stored on Amazon S3.

Further reading:

Other Options


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s