Upload Files Directly to Amazon S3 using ColdFusion
By Pete Freitag
Update 2022
ColdFusion 2021 adds support for ageneratePutPresignedUrl()
function in the aws s3 package. Another option is thegetAuthenticatedURL()
method in the s3sdk coldbox module.
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": "https://example.com/upload-complete.cfm" }, ["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"> <cfoutput> <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": "https://example.com/upload-complete.cfm" }, ["content-length-range", 1, 1048576], ["starts-with", "$Content-Type", "image/"] ] } </cfsavecontent> </cfoutput> <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="https://example-bucket-name.s3.amazonaws.com/" 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="https://example.com/upload-complete.cfm" > <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" /> </form>
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.
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:
- Running CFML on AWS Lambda with FuseLess Slides
- FCKeditor Access Denied
- Hotfix for CF8 FCKeditor Vulnerability Released
- Risks of FCKeditor Vulnerability in ColdFusion 8
The Fixinator Code Security Scanner for ColdFusion & CFML is an easy to use security tool that every CF developer can use. It can also easily integrate into CI for automatic scanning on every commit.
Try Fixinator