-
-
Save Leigh-/26993ed79c956c9309a9dfe40f1fce29 to your computer and use it in GitHub Desktop.
Amazon Web Services (AWS) S3 Wrapper for ColdFusion (Signature 4)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Amazon S3 REST Wrapper | |
* Version Date: 2016-04-12 | |
* | |
* Copyright 2015 CF Webtools | cfwebtools.com | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
* Derived from "Amazon S3 REST Wrapper" (http://amazons3.riaforge.org) by Joe Danziger ([email protected]) | |
* Requires Adobe ColdFusion 10+ equivalent (tested in Adobe ColdFusion 11) | |
* | |
* Dependency on Sv4Util.cfc (Amazon Signature 4 Utility for ColdFusion) | |
* https://gist.github.com/Leigh-/26993ed79c956c9309a9dfe40f1fce29 | |
* | |
* Changes: Modified to use Signature Version 4 (Alpha) - Leigh / cfsearching | |
* http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html | |
* | |
*/ | |
component accessors=true { | |
/** | |
* if using FW/1 & DI/1, arguments are populated using variables.framework.diConfig.constants in Application.cfc | |
*/ | |
S3v4 function init( required string S3AccessKey, required string S3SecretAccessKey ) { | |
variables.S3AccessKey = arguments.S3AccessKey; | |
variables.S3SecretAccessKey = arguments.S3SecretAccessKey; | |
variables.Sv4Util = createObject('component', 'Sv4').init(arguments.S3AccessKey, arguments.S3SecretAccessKey); | |
variables.serviceName = 's3'; | |
return this; | |
} | |
/** | |
* puts an object into the bucket | |
* ex: putObject( bucketName = 'myBucket', ACL = 'private', keyName = 'test.jpg', uploadDir = 'D:\', serverSideEncryption = 'AES256'); | |
* AWS S3 REST API: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html | |
* AWS S3 Meta Data: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html | |
* @ACL the canned ACL to apply to the object ( private | public-read | public-read-write | authenticated-read | bucket-owner-read | bucket-owner-full-control ) | |
* @bucketName the name of the target bucket | |
* @cacheControl cache the object in your CloudFront cache service for better performance. Additional fees may apply. | |
* @cacheDays how many days the object will stay in the CloudFront. Max: 100 years | |
* @contentType a standard MIME type describing the format of the contents | |
* @fileName file name to retrieve for upload (ex: myFile.jpg). If left empty it uses the keyName attribute | |
* @keyName uniquely identifies the object in the S3 bucket. Unicode characters whose UTF-8 encoding is at most 1024 bytes long. Safe: [0-9], [a-z], [A-Z], !, -, _, ., *, ', (, and ). "/" infers a folder in the S3 console. | |
* @serverSideEncryption server-side encryption algorithm to use when S3 creates an object ( none | AES256 ) | |
* @signedPayload - If true, include checksum (or hash) of request body in signature calculations. Default is true. | |
* @storageClass ( STANDARD | REDUCED_REDUNDANCY (noncritical, reproducible data at lower levels of redundancy) ) | |
* @uploadDir local path the fileName attribute is stored including trailing "/" | |
*/ | |
struct function putObject( | |
required string bucketName, | |
required string regionName, | |
string contentType = 'binary/octet-stream', | |
boolean cacheControl = false, | |
numeric cacheDays = 30, | |
string ACL = 'public-read', | |
string storageClass = 'STANDARD', | |
required string keyName, | |
string fileName = arguments.keyName, | |
string uploadDir = expandPath('./'), | |
string serverSideEncryption = 'none;', | |
boolean signedPayload = true | |
) { | |
var binaryFileData = ''; | |
var props = ''; | |
var httpService = ''; | |
var cacheSeconds = arguments.cacheDays * 24 * 60 * 60; | |
var requestHeaders = ''; | |
var requestHost = ''; | |
var headerName = ''; | |
// read the file data into a variable, sent as request body | |
// TODO: might need also use "ToBase64" per CF docs | |
binaryFileData = fileReadBinary( arguments.uploadDir & arguments.fileName ); | |
// Generate host name | |
requestHost = getHostString( arguments.bucketName ); | |
// Add desired headers. Mandatory headers (ie host and date) are added automatically | |
requestHeaders = { "X-Amz-Acl" = arguments.acl | |
, "Content-Type" = arguments.contentType | |
, "X-Amz-Storage-Class" = arguments.storageClass | |
}; | |
// Optionally, encrypt (server side) | |
if (arguments.serverSideEncryption == 'AES256') { | |
requestHeaders["x-amz-server-side-encryption"] = "AES256"; | |
} | |
// Optional, use cache control | |
if (arguments.cacheControl) { | |
requestHeaders["Cache-Control"] = "max-age=#cacheSeconds#"; | |
} | |
// Get signature properties | |
props = variables.Sv4Util.generateSignatureData( | |
requestMethod = "PUT" | |
, hostName = requestHost | |
, regionName = arguments.regionName | |
, serviceName = variables.serviceName | |
, requestURI = arguments.keyName | |
, requestBody = binaryFileData | |
, requestHeaders = requestHeaders | |
, requestParams = {} | |
, signedPayload = true | |
); | |
// send the file to amazon | |
httpService = new http(); | |
httpService.setMethod( props.requestMethod ); | |
httpService.setURL( 'https://#props.hostName##props.canonicalURI#' ); | |
httpService.addParam( type = 'header', name = 'Authorization', value=props.authorizationHeader ); | |
for (headerName in props.requestHeaders) { | |
httpService.addParam( type='header', name=headerName, value=props.requestHeaders[ headerName ] ); | |
} | |
httpService.addParam( type = 'body', value = binaryFileData); | |
return httpService.send(); | |
} | |
/** | |
* Returns a link to an object | |
* If you generate a link to a file that doesn't exist, an XML error will be returned with the code "AccessDenied", instead | |
* of a not found error, upon accessing the URL | |
* | |
* @bucketName the name of the target bucket | |
* @keyName uniquely identifies the object in the S3 bucket. Unicode characters whose UTF-8 encoding is at most 1024 bytes long. Safe: [0-9], [a-z], [A-Z], !, -, _, ., *, ', (, and ). "/" infers a folder in the S3 console. | |
* @regionName the name of the target region Example: "us-east-1" | |
* @minutesValid - The number of minutes the link should be valid | |
*/ | |
string function getObjectLink( | |
required string bucketName | |
, required string keyName | |
, required string regionName | |
, string minutesValid = 1 | |
) { | |
var props = ''; | |
var expiresInSeconds = arguments.minutesValid * 60; | |
var params = { "X-Amz-Expires" = expiresInSeconds }; | |
// Get signature properties | |
props = variables.Sv4Util.generateSignatureData( | |
requestMethod = "GET" | |
, hostName = getHostString( arguments.bucketName ) | |
, regionName = arguments.regionName | |
, serviceName = variables.serviceName | |
, requestURI = arguments.keyName | |
, requestBody = "UNSIGNED-PAYLOAD" | |
, requestParams = params | |
, requestHeaders = {} | |
, signedPayload = false | |
); | |
return "https://#props.hostName##props.canonicalURI#?"& props.canonicalQueryString &"&X-Amz-Signature="& props.signature; | |
} | |
/** | |
* Download the specified file. | |
* @bucketName the name of the target bucket | |
* @keyName uniquely identifies the object in the S3 bucket. Unicode characters whose UTF-8 encoding is at most 1024 bytes long. Safe: [0-9], [a-z], [A-Z], !, -, _, ., *, ', (, and ). "/" infers a folder in the S3 console. | |
* @regionName the name of the target region Example: "us-east-1" | |
* @path physical path to store file including trailing slash (ex: c:\myfiles\) | |
* @returns Full file path. | |
*/ | |
string function getObject( | |
required string bucketName | |
, required string keyName | |
, required string regionName | |
, string path = expandPath('./') | |
) { | |
var httpService = new http(); | |
httpService.setMethod('GET'); | |
httpService.setURL( getObjectLink( bucketName = arguments.bucketName, keyName = arguments.keyName, regionName = arguments.regionName ) ); | |
httpService.setPath(arguments.path); | |
httpService.setFile(arguments.keyName); | |
httpService.send(); | |
return arguments.path & arguments.keyName; | |
} | |
/** | |
* Deletes an object | |
* @bucketName the name of the target bucket | |
* @keyName uniquely identifies the object in the S3 bucket. Unicode characters whose UTF-8 encoding is at most 1024 bytes long. Safe: [0-9], [a-z], [A-Z], !, -, _, ., *, ', (, and ). "/" infers a folder in the S3 console. | |
* @regionName the name of the target region Example: "us-east-1" | |
*/ | |
struct function deleteObject( | |
required string bucketName | |
, required string keyName | |
, required string regionName | |
) { | |
var props = ''; | |
var httpService = ''; | |
var headerName = ''; | |
// Generate signature | |
props = variables.Sv4Util.generateSignatureData( | |
requestMethod = 'DELETE' | |
, hostName = getHostString( arguments.bucketName ) | |
, regionName = arguments.regionName | |
, serviceName = variables.serviceName | |
, requestURI = '/'& arguments.keyName | |
, requestBody = 'UNSIGNED-PAYLOAD' | |
, requestHeaders = {} | |
, requestParams = {} | |
, signedPayload = true | |
); | |
// delete the object via REST | |
httpService = new http(); | |
httpService.setMethod( props.requestMethod ); | |
httpService.setURL('https://#props.hostName##props.canonicalURI#'); | |
httpService.addParam( type = 'header', name = 'Authorization', value=props.authorizationHeader ); | |
for (headerName in props.requestHeaders) { | |
httpService.addParam( type='header', name=headerName, value=props.requestHeaders[ headerName ] ); | |
} | |
return httpService.send(); | |
} | |
/** | |
* Construct AWS hostname string | |
* @returns host name | |
*/ | |
private string function getHostString( string bucketName ) { | |
return arguments.bucketName &".s3.amazonaws.com"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment