Direct Upload to Amazon AWS S3 Using PHP & HTML

As we all know, Amazon S3 is a cost-effective, reliable, fast and secure object storage system, which allows us to store and retrieve any amount of data from anywhere on the web. Today I am going to show you how you can directly upload any file to Amazon AWS S3 using HTML Form and PHP without compromising your own server security and upload latency.

Create User and Bucket

  1. First step is to create a bucket by visiting AWS S3 Console, this is where we will be uploading files. Note down its name and AWS region.
  2. Create a IAM user profile by going in your AWS Management Console, after user creation you should be presented with unique Access Key ID and Secret Access Key, you should write them down as we will need them in our PHP code.
  3. Now create a new Policy for user in Create Policy Console (Or Copy-paste Policy Document below) and attach the Policy to IAM user you've just created.
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectAcl" ], "Resource": [ "arn:aws:s3:::*" ] } ] }
    You can also create IAM policy quickly using policy generator.
  4. Now create another "S3 Bucket policy" for your bucket using policy generator. go back to the bucket you've created, click its properties, scroll down to permissions tab and click edit bucket policy, now copy/paste policy text you've just created. Eg:
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    { "Version": "2012-10-17", "Statement": [ { "Sid": "AddPerm", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/*" } ] }

HTML Form

Once everything is set correctly in AWS console, we can now create an HTML form that can upload content to Amazon S3 directly.
HTML
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
<form action="http://<BUCKET>.s3.amazonaws.com/" method="post" enctype="multipart/form-data"> <input type="hidden" name="key" value="${filename}" /> <input type="hidden" name="acl" value="public-read" /> <input type="hidden" name="X-Amz-Credential" value="<--ACCESS_KEY-->/<--SHORT_DATE-->/<--REGION-->/s3/aws4_request" /> <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" /> <input type="hidden" name="X-Amz-Date" value="<--ISO_DATE-->" /> <input type="hidden" name="Policy" value="<--BASE64_POLICY-->" /> <input type="hidden" name="X-Amz-Signature" value="<--SIGNATURE-->" /> <input type="hidden" name="success_action_redirect" value="<--SUCCESS_REDIRECT-->" /> <input type="file" name="file" /> <input type="submit" value="Upload File" /> </form>
As you can see, there are some pre-filled texts in our form, which need to be replaced with specific values, such as Signature field (X-Amz-Signature) it requires a SHA256 calculated signature, and Policy field requires Base64-encoded policy string. We need to generate these values using PHP in the next section.

Creating POST Policy and Calculating Signature using PHP

In our PHP code we need previously created user Access Key, Secret Key and several other values such as bucket name and region, using these variables wen can construct a POST policy and calculate AWS Signature (Version 4) which are required in our HTML upload form.
PHP
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
<?php $access_key = "iam-user-access-key"; //Access Key $secret_key = "iam-user-secret-key"; //Secret Key $my_bucket = "mybucket"; //bucket name $region = "us-east-1"; //bucket region $success_redirect = 'http://'. $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; //URL to which the client is redirected upon success (currently self) $allowd_file_size = "1048579"; //1 MB allowed Size //dates $short_date = gmdate('Ymd'); //short date $iso_date = gmdate("Ymd\THis\Z"); //iso format date $expiration_date = gmdate('Y-m-d\TG:i:s\Z', strtotime('+1 hours')); //policy expiration 1 hour from now //POST Policy required in order to control what is allowed in the request //For more info http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html $policy = utf8_encode(json_encode(array( 'expiration' => $expiration_date, 'conditions' => array( array('acl' => 'public-read'), array('bucket' => $my_bucket), array('success_action_redirect' => $success_redirect), array('starts-with', '$key', ''), array('content-length-range', '1', $allowd_file_size), array('x-amz-credential' => $access_key.'/'.$short_date.'/'.$region.'/s3/aws4_request'), array('x-amz-algorithm' => 'AWS4-HMAC-SHA256'), array('X-amz-date' => $iso_date) )))); //Signature calculation (AWS Signature Version 4) //For more info http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html $kDate = hash_hmac('sha256', $short_date, 'AWS4' . $secret_key, true); $kRegion = hash_hmac('sha256', $region, $kDate, true); $kService = hash_hmac('sha256', "s3", $kRegion, true); $kSigning = hash_hmac('sha256', "aws4_request", $kService, true); $signature = hash_hmac('sha256', base64_encode($policy), $kSigning); ?>
Policy is Base64 encoded security policy that describes what is permitted in the request. For authenticated requests a policy is required, you can learn more about constructing HTTP POST Policy here.Signature is the HMAC-SHA256 hash of the security policy using AWS Signature Version 4, and is required if a policy document is included with the request, more about AWS Signature Version 4 here.

Success page

After successful upload, AWS redirects user to success page specified in success_action_redirect form field and policy document, it also attaches bucket, etag and key in the query string, which we can use to quickly generate a link to uploaded object on our S3 bucket.
PHP
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
<?php //After success redirection from AWS S3 if(isset($_GET["key"])) { $filename = $_GET["key"]; $ext = pathinfo($filename, PATHINFO_EXTENSION); if(in_array($ext, array("jpg", "png", "gif", "jpeg"))){ echo '<hr />Image File Uploaded : <br /><img src="//'.$my_bucket.'.s3.amazonaws.com/'.$_GET["key"].'" style="width:100%;" />'; }else{ echo '<hr />File Uploaded : <br /><a href="http://'.$my_bucket.'.s3.amazonaws.com/'.$_GET["key"].'">'.$filename.'</a>'; } } ?>

Putting It All Together

All we got to do now is put them together in a single PHP file.
PHP
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
<?php $access_key = "iam-user-access-key"; //Access Key $secret_key = "iam-user-secret-key"; //Secret Key $my_bucket = "mybucket"; //bucket name $region = "us-east-1"; //bucket region $success_redirect = 'http://'. $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; //URL to which the client is redirected upon success (currently self) $allowd_file_size = "1048579"; //1 MB allowed Size //dates $short_date = gmdate('Ymd'); //short date $iso_date = gmdate("Ymd\THis\Z"); //iso format date $expiration_date = gmdate('Y-m-d\TG:i:s\Z', strtotime('+1 hours')); //policy expiration 1 hour from now //POST Policy required in order to control what is allowed in the request //For more info http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html $policy = utf8_encode(json_encode(array( 'expiration' => $expiration_date, 'conditions' => array( array('acl' => 'public-read'), array('bucket' => $my_bucket), array('success_action_redirect' => $success_redirect), array('starts-with', '$key', ''), array('content-length-range', '1', $allowd_file_size), array('x-amz-credential' => $access_key.'/'.$short_date.'/'.$region.'/s3/aws4_request'), array('x-amz-algorithm' => 'AWS4-HMAC-SHA256'), array('X-amz-date' => $iso_date) )))); //Signature calculation (AWS Signature Version 4) //For more info http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html $kDate = hash_hmac('sha256', $short_date, 'AWS4' . $secret_key, true); $kRegion = hash_hmac('sha256', $region, $kDate, true); $kService = hash_hmac('sha256', "s3", $kRegion, true); $kSigning = hash_hmac('sha256', "aws4_request", $kService, true); $signature = hash_hmac('sha256', base64_encode($policy), $kSigning); ?> <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Aws S3 Direct File Uploader</title> </head> <body> <form action="http://<?= $my_bucket ?>.s3.amazonaws.com/" method="post" enctype="multipart/form-data"> <input type="hidden" name="key" value="${filename}" /> <input type="hidden" name="acl" value="public-read" /> <input type="hidden" name="X-Amz-Credential" value="<?= $access_key; ?>/<?= $short_date; ?>/<?= $region; ?>/s3/aws4_request" /> <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" /> <input type="hidden" name="X-Amz-Date" value="<?=$iso_date ; ?>" /> <input type="hidden" name="Policy" value="<?=base64_encode($policy); ?>" /> <input type="hidden" name="X-Amz-Signature" value="<?=$signature ?>" /> <input type="hidden" name="success_action_redirect" value="<?= $success_redirect ?>" /> <input type="file" name="file" /> <input type="submit" value="Upload File" /> </form> <?php //After success redirection from AWS S3 if(isset($_GET["key"])) { $filename = $_GET["key"]; $ext = pathinfo($filename, PATHINFO_EXTENSION); if(in_array($ext, array("jpg", "png", "gif", "jpeg"))){ echo '<hr />Image File Uploaded : <br /><img src="//'.$my_bucket.'.s3.amazonaws.com/'.$_GET["key"].'" style="width:100%;" />'; }else{ echo '<hr />File Uploaded : <br /><a href="http://'.$my_bucket.'.s3.amazonaws.com/'.$_GET["key"].'">'.$filename.'</a>'; } } ?> </body> </html>
That's it! you can copy this code or download and play with your own Aws S3 credentials . You can also check-out the demo page that directly uploads files in my S3 bucket, Good luck!Next : Ajax Upload to Amazon AWS S3 Using jQuery & PHP
Download Demo
  • Hello and thank you for this piece of code.I just tested it in my local environment and works perfectly.Is there a way to upload only certain filetypes and automagically rename files so if there's a file with same name i can upload both?I wish my users can upload files to my S3 (due to the virus emergency, so i can download and work on them from home).Thank you!!
  • It is easier to connect with s3 when the PHP site is hosted on Ec2. I have a SaaS app on EC2 through Cloudways. All the backups are automatically stored to s3.
  • Hi there, What do I have to change if I want images to embed as opposed to downloading when I navigate to their direct URLs?
  • Hello San, I downloaded the sample code and put it in my htdocs folder of my xampp server and followed all the steps as you have mentioned. Put in my bucket name, access key and secret key. Yet when i run the file and click on browse and select the file and click upload to upload the file, nothing happens. When i click on submit, nothing happens at all.And when i go to my S3 BUCKET and check there is nothing inside it. It is empty. I dont get a success message nor a redirect.Please help
  • Hey, Thank you so much for the perfect documentation! the code is working perfectly fine if i am just adding an image but, in my form i want to add data along with image. I want to add the data to the database along with image url and along with this i want to upload the image to my s3 bucket. kindly help me with the code. How can i do both the task at one click?
    • See "success_action_redirect", it redirects to URL you specify after a successful upload. You can program that page to capture some of the query string returned by the server and save it to a database.