Pete Freitag Pete Freitag

Upload Files Directly to Amazon S3 using ColdFusion


Here's a quick example showing how to upload a file directly to Amazon S3 (bypassing your server). The tricky part in getting this to work is that you don't want to allow anyone to upload a file anywhere on your S3. To accomplish this you can create an AWS Access Control Policy, base64 encode it, and then sign it using HMAC-SHA1 with your AWS Secret Key. A policy is a JSON string that might look like this:

{ "expiration": "2014-11-26T13:23:00.000Z",
  "conditions": [
    {"bucket": "example-bucket-name" },
    ["eq", "$key", "image.jpg"],
    {"acl": "public-read" },
    {"redirect": "" },
    ["starts-with", "$Content-Type", "image/"]

To generate this policy dynamically we might do something like this:

<cfset expDate = DateConvert("local2utc", now())>
<cfset expDate = DateAdd("n", 15, expDate)><!--- policy expires in 15 minutes --->
<cfset fileName = CreateUUID() & ".jpg">
<cfsavecontent name="jsonPolicy">
{ "expiration": "#DateFormat(expDate, "yyyy-mm-dd")#T#TimeFormat(expDate, "HH:mm")#:00.000Z",
  "conditions": [
    {"bucket": "example-bucket-name" },
    ["eq", "$key", "#JSStringFormat(fileName)#"],
    {"acl": "public-read" },
    {"redirect": "" },
    ["content-length-range", 1, 1048576],
    ["starts-with", "$Content-Type", "image/"]
<cfset b64Policy = toBase64(Trim(jsonPolicy), "utf-8")>
<cfset signature = HMac(b64Policy, variables.awsSecretKey, "HMACSHA1", "utf-8")>
<!--- convert signature from hex to base64 --->
<cfset signature = binaryEncode( binaryDecode( signature, "hex" ), "base64")>

Because we are using the HMac function you must be on CF10+ or Railo 4.1+ if you are on an older version you will need to find a third party hmac implementation.

Next you create a form that posts directly to Amazon S3:

<form action="" method="post" enctype="multipart/form-data">
    <input type="hidden" name="key" value="#EncodeForHTMLAttribute(fileName)#" /
    <input type="hidden" name="acl" value="public-read" />
    <input type="hidden" name="redirect" value="" >
    <input type="hidden" name="AWSAccessKeyId " value="#EncodeForHTMLAttribute(variables.awsAccessKeyID)#" />
    <input type="hidden" name="Policy" value="#b64Policy#" />
    <input type="hidden" name="Signature" value="#signature#" />
    File: <input type="file" name="file" />
    <input type="submit" name="submit" value="Upload to Amazon S3" />

According to the S3 documentation there are some conditions in which the redirect will not happen:

Please note that the redirect is not guaranteed to be followed. It is possible that an upload would succeed, but that a networking problem on the end-users network prevents them from following the redirect. It is also possible that in certain failure conditions, that a file is actually uploaded but you are not notified about the upload.

To get around this uncertainty you can setup Event Notifications for the S3 bucket you are uploading to.

The other thing to note is that if an error occurs the redirect will not happen and the user will be presented with an XML error message. To handle that more gracefully you could upload the file via AJAX, and then handle the error condition within JavaScript.

Like this? Follow me ↯

Upload Files Directly to Amazon S3 using ColdFusion was first published on November 26, 2014.

If you like reading about aws, s3, upload, coldfusion, or hmac then you might also like:

FuseGuard Web App Firewall for ColdFusion

The FuseGuard Web Application Firewall for ColdFusion & CFML is a high performance, customizable engine that blocks various attacks against your ColdFusion applications.