Shortdark Software Development

Interacting with S3 using PHP

Development9th Feb 2019.Time to read: 5 mins

AWSIAMJSONPHPS3

Trying to connect to use AWS S3 for the first time can be confusing. Here is a quick guide to roughly what has to happen.

Basic Steps to Set up S3

The only two sections you need in the AWS console for this are "S3" and "IAM"...

  • Create a S3 bucket.
  • Make a S3 bucket policy.
  • Create a policy to access the bucket in IAM.
  • Create a "programmatic-only" user for the bucket and attach this policy to it (IAM).

Store the info for your user (the secret will not be displayed a second time). You'll use this to connect with the S3 instance in your code.

Easy, huh?

AWS is pretty good at telling you when you're about to do something stupid during this process. There are plenty of warning signs on the screen if you make anything public. AWS is all about security and particularly dislikes us making things public that should not be public.

The bucket policy and user policy are both in JSON format.

There is a website to help you make the correct JSON but the form itself is pretty good at telling you if there is an error in your policy or you've made your bucket public. An example Bucket policy might look like...

{
    "Version": "2012-10-17",
    "Id": "Policy123465789",
    "Statement": [
        {
            "Sid": "Allow ALL access to the bucket by one user",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:user/myusername"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::my-bucket-name",
                "arn:aws:s3:::my-bucket-name/*"
            ]
        }
    ]
}

The user policy (IAM) is created by a wizard but then if you want to edit the policy you see that it is also a piece of JSON. Each action is listed in the JSON so it would be very easy indeed to simply delete the actions in the JSON, or just unselect a specific action when you're creating the policy.

AWS S3 Docs

The AWS S3 docs for PHP are pretty extensive but getting to the exact thing you need is not always straightforward. I found that I was making a lot of google searches in order to find what I was looking for because the navigation wasn't that great.

This page on the AWS SDK for PHP S3StreamWrapper was particularly well laid out and useful. The main AWS SDK for PHP docs are pretty extensive as long as you know the name of the function you want to use.

Also, if you do a search for instructions on how to do a certain thing, like connect to S3, make sure the article is fairly up-to-date. Earlier versions allow you to connect in different ways that may not always work with the current version of the AWS SDK. Other things may be similar/the same.

As with most programming, there are often multiple ways to complete the same task. For example, in the example below there are at least two ways to output the JPEGs to the screen via PHP (see comments in the code).

You can use this PHP code in any AWS instance that you can code PHP in. I added this to my Lightsail instance but it would also work on EC2 or with any other non-AWS hosting...

S3 Gallery and JPEG Displayer Example

This is a simple code to turn every file in every bucket into a gallery. This is the code to create the list of "thumbs". In this case, the thumbs are the large image but made smaller with CSS.

<?php

// Require the Composer autoloader.
require '../vendor/autoload.php';

use Aws\S3\S3Client;

try {

    // Instantiate the S3 client with your AWS credentials
    $s3Client = S3Client::factory(array(
        'version' => 'latest',
        'region'  => 'eu-west-2',
        'credentials' => array(
            'key'    => 'unique_string', // From AWS IAM user
            'secret' => 'unique_secret_string' // From AWS IAM user
        )
    ));

    //Listing all S3 Bucket
    $buckets = $s3Client->listBuckets();
    foreach ($buckets['Buckets'] as $bucket) {
        $bucket = $bucket['Name'];
        $objects = $s3Client->getIterator('ListObjects', array(
            "Bucket" => $bucket
        ));

        // Show each one 200x200 and link to full-size file...
        foreach ($objects as $myobject) {
            echo "<p><a href=\"/showitem.php?item={$myobject['Key']}\"><img src=\"/showitem.php?item={$myobject['Key']}\" style=\"height: 200px; width: 200px;\"></a></p>\n";
        } // end foreach
    } // end foreach


}catch(Exception $e) {
    // Only show this for testing purposes...
   exit($e->getMessage());
}

Then, to display the files from the S3 bucket, we do not want to have the AWS S3 URL in the browser, so we're going to display the images through PHP with the showitem.php file. Here is the code for that file, it's a very simple image displayer...

<?php

// Require the Composer autoloader.
require '../vendor/autoload.php';

use Aws\S3\S3Client;

$bucket = "my-bucket-name";

try {

    // Instantiate the S3 client with your AWS credentials
    $s3Client = S3Client::factory(array(
        'version' => 'latest',
        'region'  => 'eu-west-2',
        'credentials' => array(
            'key'    => 'unique_string', // From AWS IAM user
            'secret' => 'unique_secret_string' // From AWS IAM user
        )
    ));

    $s3Client->registerStreamWrapper();

    if(isset($_GET['item'])){
        $keyname= filter_var($_GET['item'], FILTER_SANITIZE_STRING);

        // Get the object.
        $result = $s3Client->getObject([
            'Bucket' => $bucket,
            'Key'    => $keyname
        ]);

        // Display the object in the browser.
        $type = $result['ContentType'];
        $size = $result["ContentLength"];
        header('Content-Type:'.$type);
        header('Content-Length: ' . $size);
        echo $result['Body'];

        // Alternatively, get file contents from S3 Bucket like this...
        // $data = file_get_contents('s3://'.$bucket.'/'.$keyname);
        // echo $data;
    }

}catch(Exception $e) {
    // Only show this for testing purposes...
    exit($e->getMessage());
}

When you add a file to S3, you're probably either doing it programmatically, or you're dragging and dropping into the S3 Browser. When you want to use the file, you'll find that you can get quite a lot of information from the getObject() method that is listed in the docs. I wanted to find the exact response for content length, so you would just look up getObject and see what is returned.

This is a very quick example. There are some pretty major things here that would be better doing them a different way. For example, when we connect to S3 with the factory() method, we should probably use the .aws/credentials file to make one or more profiles so that our secret info isn't listed in the PHP of public part of the website.

Also, S3 can be accessed with the AWS CLI


Previous: Apache to NginxNext: How to create and run Bash Scripts