As my first foray into open-source code, I'm releasing a wrapper for interacting with Amazon's Simple Storage Service (S3) via REST. The wrapper is packaged as a CFC and has the following methods:
- init(accessKeyID, secretAccessKey) - initialize CFC (both parameters required).
- getBuckets() - List all buckets.
- putBucket(bucketName) - create a new bucket.
- getBucket(bucketName, prefix, marker, maxKeys) - get contents of a bucket (prefix
is optional and matches on the beginning of a key, marker is optional and results
start from there, maxKeys is optional and restricts the number of objects returned).
- deleteBucket(bucketName) - delete a bucket (bucket must be empty).
- putObject(bucketName, fileKey, contentType, HTTPtimeout) - puts an object into a
bucket (HTTPtimeout is in seconds).
- getObject(bucketName, fileKey, minutesValid) - get link to an object (minutesValid
is optional and defaults to 60).
- deleteObject(bucketName, fileKey) - delete an object from a bucket.
A simple test script is included which demonstrates the use of the CFC. You must insert your Amazon S3 access keys in the first 2 lines in s3test.cfm, then just pull it up in a browser.
This is an initial release. Future plans include support for Access Control Lists. If you need something else added, let me know.
This script should run on both ColdFusion MX 6 and 7, let me know if you run into any problems.
The current version is 1.1 and you can visit the project page and download here.
S3 is definitely a great service and I've recently been able to move to a hosting provider after 10+ years of running my own servers now that I don't have to worry about the storage equation and scaling up. Stay tuned for an article on this wrapper in the next CFDJ..
Do you know if you have to put the link within a cfcontent and or cfheader tag?
Thank you again.
Thanks for your solution. It cleared up the issue for another user as well and I've added made a 1 line fix to the CFC which clears everything up. Basically, line 276 now includes the URLEncodedFormat, like such: <cfset signature = URLEncodedFormat(ToBase64(Hex2Bin("#digest#")))>
Thanks for Charles K. for verifying the fix. Hopefully that's the last bug! :)
Any suggestions?
Thanks!
Thanks,
David.
How would you keep this from happening? It seems if you were retrieving the information it could be several times a second if multiple users hit different pages.
Great Library by the way seems to work wonderfully. Have you done anything with private objects?
The request signature we calculated does not match the signature you provided. Check your key and signing method.
Anyone else have this problem?
Thanks so much for your prompt reply. The readme file I have says version 1.2, is that not the latest release?
Also, I don't plan on doing any deleting, but just so you know, BinaryDecode isn't available in MX6.
Thanks,
Dominic
As for MX6, there is a Hex2Bin function that can replicate the functionality. It may just be commented out in the released version, but let me know if you need it and it's not and I can probably find it somewhere.
I just downloaded the package from the link on this page for the first time yesterday, and updated this morning as well, but I don't see any URLEncodedFormat(). Obviously it's just as easy for me to add it myself, but I thought I'd let you know.
Also, in the CFC I have now, all the functions use Hex2Bin (which is included) except for deleteObject, which uses binaryDecode. Again, I can make the change myself, but I thought you'd like to know. FWIW, the CFC I have says
Version 1.2 - Released: October 4, 2006
Thanks again,
Dominic
This looks terrific. Everything works for me, except one thing: when I click on a link the file is downloaded, but it's still in an encrypted format. (I made the change for URLEncodedFormat -- that's not the problem).
I can see my files, I can get to the link, but when it downloads, the file is still encrypted. Any thoughts? I have all the hmac files in the right place. (I'm using BlueDragon 6, but that shouldn't be a problem, I don't think).
Appreciate any help. This looks like a terrific wrapper. Thanks!
I did make 2 changes to the CFC that may be helpful.
1) Changed binaryDecode to Hex2Bin on about line 303 (as mentioned above), and
2) Also needed to add "ToString" on about line 249, as follows:
<cfhttpparam type="body" value="#ToString(binaryFileData)#">
Otherwise it was throwing an error, "Cannot convert binary data to string". Might be BlueDragon specific, but now everything is working perfectly.
Great CFC, Joe. Thank you!
<cfhttp method="PUT" url="http://s3.amazonaws.com/#arguments.bucketName#/tes...;
timeout="#arguments.HTTPtimeout#">
The subdirectory /test/ does exist, file upload seems to go thru but file never gets there.
one question.
will this work on ColdFusion MX 7 Standard edition (one server) ?
Or you need a enterprise edition in order to access amazon S3 service ?
let me know
poz
ob1
Also need to edit this line with subdirectory name:
<cfset var cs = "PUT\n\n#arguments.contentType#\n#dateTimeString#\nx-amz-acl:public-read\n/#arguments.bucketName#/#arguments.fileKey#">
I've created a var that I update and have inserted in the line above:
<cfset subPath = "test/">
<cfset var cs = "PUT\n\n#arguments.contentType#\n#dateTimeString#\nx-amz-acl:public-read\n/#arguments.bucketName#/#subPath##arguments.fileKey#">
Use trailing slash in case subPath is blank, so will not break if no sub path specified.
Quick and dirty.
@bhbidder: Be careful with your bucket names - bucket names are unique across the enture Amazon S3 platform, so that is why test would be a problem - someone already created one with that name.
ex. \mybucket\a\
just curoius if you have thought about adding Logging functionality to it?
the Amazon documentation for enabeling logging is pretty wacky...i might take a stab at it if you don't have any plans to do it...
<cfset epochTime = DateDiff("s", DateConvert("utc2Local", "January 1 1970 00:00"), CreateDateTime(Year(now())+1,"12","20","00","00","00") ) >
Thought it might help others.
I have enhanced this cfc with some new features.
-The ability to set an ACL when you put a bucket or object.
-A getAcl function to return groups and user Acl's.
-A putBucketLogging to enable and disable logging.
-A getBucketLogging to retrieve the current logging status.
I'll email Joe to see if he is happy to add it to his CFC to save creating multple versions.
Much thanks!
Perry
Any help greatley appreciated
Thanks again for this amazing script.. i'm stoked at the moment!!
By the way.. you guys did know you can also reach your files thru the following construction... http://bucketnamet.s3.amazonaws.com/name-of-the-fi...
While recently working on a project using Amazon S3, I developed the need to be able to edit the ACL of the buckets I was adding to my S3 account.
Following on from the great work by Joe, I have added ACL compatibility to the putBucket method.
For those of you who are interested, you can download the edited version here - http://www.stevehicksonline.com/2007/06/04/amazon-...
Steve
Prefix must resolve to a namespace:
Has anyone ever seen this before? Id really like to start using s3
As for these "folders", anyone know if you somehow have subfolders within a folder?
@Joe, could you please update the code? You said you fixed it in 1.2, but the only link I can find is to the older version.
<cfset timedAmazonLink = "http://s3.amazonaws.com/#arguments.bucketName#/#ar...(signature)#">
Hope that helps...
Here is the function:
<cffunction name="deleteBucket" access="public" output="false" returntype="string"
description="Deletes a bucket.">
<cfargument name="bucketName" type="string" required="yes">
<cfset var signature = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send based on operation requested --->
<cfset var cs = "DELETE\n\n\n#dateTimeString#\n/#arguments.bucketName#">
<!--- Replace "\n" with "chr(10) to get a correct digest --->
<cfset var fixedData = replace(cs,"\n","#chr(10)#","all")>
<!--- Calculate the hash of the information --->
<cf_hmac hash_function="sha1" data="#fixedData#" key="#variables.secretAccessKey#">
<!--- fix the returned data to be a proper signature
<cfset signature = ToBase64(Hex2Bin("#digest#"))> --->
<cfset signature = URLEncodedFormat(ToBase64(Hex2Bin("#digest#")))>
<!--- delete the bucket via REST --->
<cfhttp method="DELETE" url="http://s3.amazonaws.com/#arguments.bucketName#&quo...; charset="utf-8">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfreturn cfhttp.header>
</cffunction>
Any suggestions welcome,
Andrew.
http://www.coldfusiondeveloper.com.au/go/blog/2008...
and then:
<cfset signature = ToBase64(HMAC_SHA1(variables.secretAccessKey,fixedData)) />
for compression, text uploads, folders, logging, using a proxies (aka fidler2) and a lot of other little things here and there
I also changed the behaviour to return the cfhttp result so it can be properly inspected
for the correct status codes and a dev level response status code checker