BoxLang 🚀 A New JVM Dynamic Language Learn More...

Amazon S3 SDK

v6.0.0+3 Modules

AWS S3 SDK CI

Total Downloads Latest Stable Version Apache2 License

Amazon S3 SDK

This SDK will provide you with Amazon S3 connectivity for any ColdBox, BoxLang or CFML Application. It also works great as a standalone library outside of ColdBox, and is fully compatible with S3-compatible services like DigitalOcean Spaces, Google Cloud Storage and MinIO.

Built natively for BoxLang, and fully backwards compatible with Lucee and Adobe ColdFusion (ACF).

Features

  • First-class BoxLang support, running natively or in CFML compatibility mode
  • Works with Lucee and Adobe ColdFusion (ACF)
  • Full ColdBox Module integration with WireBox injection DSL: AmazonS3@s3sdk
  • Also usable 100% standalone, outside of ColdBox
  • AWS Signature Version 4 (default) and Version 2 support
  • Compatible with any S3-compatible endpoint: Amazon S3, DigitalOcean Spaces, Google Cloud Storage, MinIO, etc
  • Bucket operations: create, list, delete, ACLs, versioning, lifecycle rules
  • Object operations: put, get, copy, rename, delete, metadata, streaming downloads
  • Multi-part uploads for large files with configurable concurrency, keeping memory usage low
  • Pre-signed URL generation for both GET and PUT operations, so clients can upload/download directly to/from S3 without proxying through your server
  • Server-side encryption support (SSE-C / SSE-S3)
  • Automatic retries on 500/503 responses with configurable retry counts
  • LogBox integration for full request/response debugging

Resources

Requirements

  • BoxLang 1+ (native or CFML compatibility mode)
  • Lucee 6+
  • Adobe ColdFusion 2023+
  • ColdBox 8+ (only if used as a ColdBox Module)

Installation

This SDK can be installed as a standalone library or as a ColdBox Module. Either approach requires a simple CommandBox command:

box install s3sdk

AI Skills

The repository's BoxLang AI skills are ignored from version control. Install them locally with:

npx skills experimental_install

Then follow either the standalone or module instructions below.

Standalone Usage

This SDK will be installed into a directory called s3sdk and can be instantiated directly via new s3sdk.models.AmazonS3():

s3 = new s3sdk.models.AmazonS3(
	accessKey = "your-access-key",
	secretKey = "your-secret-key",
	awsRegion = "us-east-1"
);

// Create a bucket
s3.createBucket( bucketName = "my-bucket" );

// Upload a file
s3.putObjectFile(
	bucketName = "my-bucket",
	filepath   = "/path/to/file.pdf",
	uri        = "documents/file.pdf"
);

// Generate a pre-signed download URL valid for 5 minutes
url = s3.getAuthenticatedURL(
	bucketName = "my-bucket",
	uri        = "documents/file.pdf",
	minutesValid = 5
);

// Delete an object
s3.deleteObject( bucketName = "my-bucket", uri = "documents/file.pdf" );

Full constructor reference:

/**
 * Create a new S3SDK Instance
 *
 * @accessKey The Amazon access key.
 * @secretKey The Amazon secret key.
 * @awsDomain The Domain used S3 Service (amazonws.com, digitalocean.com, storage.googleapis.com). Defaults to amazonws.com
 * @awsRegion The Amazon region. Defaults to us-east-1 for amazonaws.com
 * @encryptionCharset The charset for the encryption. Defaults to UTF-8.
 * @signature The signature version to calculate, "V2" is deprecated but more compatible with other endpoints. "V4" requires Sv4Util.cfc & ESAPI on Lucee. Defaults to V4
 * @ssl True if the request should use SSL. Defaults to true.
 * @defaultTimeOut Default HTTP timeout for all requests. Defaults to 300.
 * @defaultDelimiter Delimter to use for getBucket calls. "/" is standard to treat keys as file paths
 * @defaultBucketName Bucket name to use by default
 * @defaultCacheControl Default caching policy for objects. Defaults to: no-store, no-cache, must-revalidate
 * @defaultStorageClass Default storage class for objects that affects cost, access speed and durability. Defaults to STANDARD.
 * @defaultACL Default access control policy for objects and buckets. Defaults to public-read.
 * @autoContentType Tries to determine content type of file by file extension. Defaults to false.
 * @autoMD5 Calculates MD5 hash of content automatically. Defaults to false.
 * @debug Used to turn debugging on or off outside of logbox. Defaults to false.
 * @defaultEncryptionAlgorithm The default server side encryption algorithm to use.  Usually "AES256". Not needed if using custom defaultEncryptionKey
 * @defaultEncryptionKey	The default base64 encoded AES 356 bit key for server side encryption.
 * @urlStyle					 Specifies the format of the URL whether it is the `path` format or `virtual` format. Defaults to path. For more information see https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html
 *
 * @return An AmazonS3 instance.
 */
public AmazonS3 function init(
	required string accessKey,
	required string secretKey,
	string awsDomain = "amazonaws.com",
	string awsRegion = "us-east-1",
	string encryptionCharset = "UTF-8",
	string signature = "V4",
	boolean ssl = true,
	string defaultTimeOut= 300,
	string defaultDelimiter='/',
	string defaultBucketName='',
	string defaultCacheControl= "no-store, no-cache, must-revalidate",
	string defaultStorageClass= "STANDARD",
	string defaultACL= "public-read",
	boolean autoContentType= false,
	boolean autoMD5= false,
	boolean debug= false,
	string defaultEncryptionAlgorithm = "",
	string defaultEncryptionKey = "",
	string urlStyle	= "path"
)

ColdBox Module

This package is also a ColdBox module. Configure it by creating an s3sdk configuration structure in your moduleSettings struct in config/Coldbox.cfc:

moduleSettings = {
	s3sdk = {
		// Your amazon, digital ocean access key
		accessKey = "",
		// Tries to determine content type of file by file extension when putting files. Defaults to false.
		autoContentType = false,
		// Calculates MD5 hash of content automatically. Defaults to false.
		autoMD5 = false,
		// Your AWS/Digital Ocean Domain Mapping: defaults to amazonaws.com
		awsDomain = "amazonaws.com",
		// Your AWS/Digital Ocean Region: Defaults to us-east-1
		awsregion = "us-east-1",
		// Used to turn debugging on or off outside of logbox. Defaults to false.
		debug = false,
		// Default access control policy for objects and buckets. Defaults to public-read.
		defaultACL = "public-read",
		// The default bucket name to root the operations on.
		defaultBucketName = "",
		// Default caching policy for objects. Defaults to: no-store, no-cache, must-revalidate
		defaultCacheControl = "no-store, no-cache, must-revalidate",
		// The default delimiter for folder operations
		defaultDelimiter = "/",
		// Default storage class for objects that affects cost, access speed and durability. Defaults to STANDARD.
		// AWS classes are: STANDARD,STANDARD_IA,INTELLIGENT_TIERING,ONEZONE_IA,GLACIER,DEEP_ARCHIVE
		// Google Cloud Storage Clases: regional,multi_regional,nearline,coldline,
		defaultStorageClass = "STANDARD",
		// Default HTTP timeout in seconds for all requests. Defaults to 300 seconds.
		defaultTimeOut = 300,
		// The default encryption character set: defaults to utf-8
		encryptionCharset = "utf-8",
		// How many times to retry the request before failing if the response is a 500 or 503
		retriesOnError		: 3,
		// Your amazon, digital ocean secret key
		secretKey = "",
		// Service name that is part of the service's endpoint (alphanumeric). Example: "s3"
		// Only used for the v4 signatures
		serviceName         : "s3",
		// The signature version to calculate, "V2" is deprecated but more compatible with other endpoints. "V4" requires Sv4Util.cfc & ESAPI on Lucee. Defaults to V4
		signature = "V4",
		// SSL mode or not on cfhttp calls and when generating put/get authenticated URLs: Defaults to true
		ssl = true,
		// Throw exceptions when s3 requests fail, else it swallows them up.
		throwOnRequestError : true,
		// What format of endpoint to use whether path or virtual
		urlStyle = "path"
	}
};

Then leverage the SDK via the WireBox injection DSL: AmazonS3@s3sdk

component {

	property name="s3" inject="AmazonS3@s3sdk";

	function index( event, rc, prc ){
		s3.putObjectFile(
			bucketName = "my-bucket",
			filepath   = "/path/to/file.pdf"
		);
	}

}

S3-Compatible Services

Since this SDK speaks the standard S3 REST API, it works out of the box with any S3-compatible storage provider by simply changing the awsDomain setting:

Provider awsDomain
Amazon S3amazonaws.com (default)
DigitalOcean Spacesdigitaloceanspaces.com
Google Cloud Storagestorage.googleapis.com
MinIO / self-hostedyour MinIO endpoint hostname

Usage

Please check out the full API docs: https://apidocs.ortussolutions.com/#/coldbox-modules/s3sdk/, choose your version and code away!

Running the Tests

This module ships with a test-harness and can be tested against any of the supported engines using CommandBox:

box install
box server start serverConfigFile="[email protected]"
box testbox run

See .github/workflows/tests.yml for the full CI matrix, which runs against native BoxLang, BoxLang with CFML compatibility, Lucee and Adobe ColdFusion.

Development

See Contributing and AGENTS.md for guidance on developing and testing this module, including for AI coding agents.

Contributing

Pull requests are welcome! Please make sure any changes pass box run-script format:check and the full test suite before submitting.


© Ortus Solutions, Corp

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.


Unreleased

6.0.0 - 2026-09-15

Added

  • AI Skills Integration
  • Native BoxLang (boxlang@1) server and CI matrix entry, in addition to the existing boxlang-cfml@1 (CFML compatibility) entry

Changed

  • CI test matrix now covers boxlang@1, boxlang-cfml@1, lucee@6 and adobe@2023/adobe@2025. Dropped lucee@5 and adobe@2018/adobe@2021 (EOL)
  • Minimum ColdBox version bumped to ^8
  • devDependencies : removed commandbox-dotenv and commandbox-cfconfig, added commandbox-boxlang
  • Module description updated to: "This SDK will provide you with Amazon S3 connectivity for any ColdBox, BoxLang or CFML Application."
  • readme.md rewritten and expanded, with BoxLang as the preferred/first-class engine
  • test-harness/box.json : testbox devDependency bumped from be to * to pick up TestBox's isBoxLang()/isLucee()/isAdobe() engine-detection helpers (and the 7.1.0 fix for isLucee() incorrectly returning true on BoxLang)
  • test-harness/tests/specs/AmazonS3Spec.cfc : replaced ad-hoc engine checks (structKeyExists( server, "lucee" ), isNull( server.lucee ), server.keyExists( "boxlang" )) with TestBox's isAdobe()/isLucee()/isBoxLang()
  • The 6 "customer encryption key" (SSE-C) specs in AmazonS3Spec.cfc now exercise SSE-S3 (encryptionAlgorithm) instead of SSE-C (encryptionKey), since the CI test bucket's policy blocks SSE-C uploads. The SDK's SSE-C support itself (encryptionKey argument) is unchanged for callers whose bucket allows it

Fixed

  • Set all hash usage algorithms to MD5 for Adobe change to default algorithm
  • Sv4Util.cfc, Sv2Util.cfc : optional amzDate/dateStamp override arguments were checked with structKeyExists( arguments, ... ), which is unreliable on engines with full-null support like BoxLang (an unpassed argument still exists as a null key). Now checked independently with isNull(), fixing spurious SignatureDoesNotMatch errors on BoxLang
  • MiniLogBox.cfc : same isNull() fix applied to the optional data argument on debug(), error() and warn()
  • Sv4Util.cfc, Sv2Util.cfc : the UTC date stamp was generated with dateFormat( utcDateTime, "yyyymmdd" ). Lowercase mm is minutes, not month, on some engines. Corrected to yyyyMMdd
  • copyObject() ( and therefore renameObject(), which calls it internally ) manually set a Content-Length: 0 header that duplicated the header CFHTTP already sends for a bodyless request. Adobe CF and BoxLang do not de-duplicate this, sending content-length: 0,0 on the wire and breaking AWS's SignatureDoesNotMatch validation. Removed the redundant header
  • [email protected] had leftover module aliases (/moduleroot/cbfs) copied from another module; corrected to /moduleroot/s3sdk
  • test-harness/tests/specs/AmazonS3Spec.cfc : isOldACF() unconditionally read server.coldfusion.productVersion, which doesn't exist on native BoxLang, crashing the whole test bundle on boxlang@1. Now guarded with structKeyExists( server, "coldfusion" )
  • CI : force-install the latest commandbox-cfconfig before starting servers, since the version bundled with the CommandBox CLI has no config provider for adobe@2025 yet
  • Sv4UtilSpec.cfc test fixture helpers used .listToArray() member-function syntax, which Adobe ColdFusion doesn't resolve the same way Lucee/BoxLang do ("The listToArray method was not found"). Switched to the top-level listToArray( string, delimiter ) function call, which is portable across all three engines
  • putObjectFile()'s multi-part upload path called java.nio.file.Files.newByteChannel( path, [] ) with an untyped, empty CFML array for the varargs OpenOption... parameter. Explicitly javacast( "java.nio.file.OpenOption[]", [] ) now, for safer Java interop (this alone did not fix the underlying multi-part failure on Adobe; see below)
  • CI : Setup Java was pinned to Java 11, but Adobe ColdFusion 2025's cfpm tooling requires Java 17+ (UnsupportedClassVersionError: ... class file version 61.0 ... only recognizes ... up to 55.0). Bumped to Temurin 17
  • [email protected] (native BoxLang) didn't install the bx-esapi module, so any call to encodeForURL() (used by Sv4Util.cfc's urlEncodePath()) failed with Function [encodeForURL] not found, crashing the entire AmazonS3Spec bundle at beforeAll(). Added onServerInitialInstall: install bx-esapi, matching [email protected]
  • requireBucketName(), getBucketLocation(), createBucket(), objectExists(), getAuthenticatedURL() and applyACLHeaders() called throw() without an explicit type. Adobe/Lucee default the type to Application, but BoxLang defaults it to Custom, breaking tests asserting toThrow( type = "application" ). All now throw an explicit type = "Application"
  • [email protected]/[email protected] : pinned the server's own JVM to javaVersion: openjdk21_jre, matching the BoxLang server configs, for consistent Java 21 runtime behavior across engines
  • Sv4UtilSpec.cfc test fixture helpers named a parameter file, which is treated specially on Adobe ColdFusion ("Complex object types cannot be converted to simple values" when passed into listToArray()). Renamed to requestContent
  • [email protected]/[email protected] : added JVM arg --add-opens java.base/sun.nio.fs=ALL-UNNAMED, matching the working config in coldbox-modules/cbfs, for safer Java NIO reflection on Java 17+
  • CI : replaced the separate Ortus-Solutions/setup-commandbox action plus manual box install --force commandbox-boxlang/commandbox-cfconfig steps with ortus-boxlang/setup-boxlang@main (with-commandbox: true, installing commandbox-boxlang, commandbox-cfconfig and testbox-cli), matching coldbox-modules/cbfs's setup. This also fixed adobe@2025's server failing to start. Also bumped Setup Java from 17 to 21, matching cbfs and the server JVM pins
  • putObjectFile()'s multi-part upload path optionally routed concurrent part uploads through variables.asyncManager.allApply(). On Adobe, ColdBox's async cbproxies Function wrapper does not correctly marshal the part struct argument across the async boundary, throwing coldfusion.runtime.UndefinedElementException: Element UPLOADID is undefined in PART inside the closure. This was silently caught by the surrounding try/catch and fell back to a non-multipart upload, with no visible error ("can perform a multi-part upload on a file over 5MB" failing only with the response not containing "multipart"). Always use the synchronous part-upload path now, until the ColdBox/Adobe async interop issue is resolved upstream
  • AmazonS3Spec.cfc's multi-part upload test pre-computed the expected uploaded file size before calling fileWrite(), then asserted the S3 object's Content-Length against that pre-computed value. On adobe@2025 the file written to disk was 1 byte larger than expected, failing the assertion. Now reads the actual on-disk size via getFileInfo() after writing, so the assertion is correct regardless of any engine-specific fileWrite() behavior

v5.7.1 => 2023-SEP-21

Fixed

  • Added entryPoint, modelNamespace and cfmapping keys to ModuleConfig, to ensure mappings for downstream modules are available during framework load

v5.7.0 => 2023-MAY-03

Changed

  • Updates permission handling to account for updated AWS default bucket policies

v5.6.0 => 2023-MAR-07

Added

  • Support for overriding response headers like content type for pre-signed URLs

v5.5.2 => 2023-FEB-07

Fixed

  • Multi-part upload concurrency fixes

v5.5.1 => 2023-FEB-03

Added

  • Support for multi-part file uploads to conserve memory usage

v5.4.1 => 2023-FEB-02

v5.3.1 => 2023-FEB-02

v5.2.0 => 2023-JAN-26

Added

  • Add support for server side encryption
  • Add retry support for S3 connection failures

v5.1.2 => 2022-OCT-19

Added

  • Added property to ensure URLEndpointHostname can be retreived

v5.1.1 => 2022-NOV-1

Fixed

  • Fixes an issue when header content types were not present in the arguments scope

v5.0.0 => 2022-OCT-19

Changed / Compatibility

  • Dropped Adobe 2016 Support
  • Configuration setting: encryption_charset changed to encryptionCharset for consistency. Breaking change

Added

  • Revamp of ACLs to allow any grant to be added to any object.
  • Ability to request PUT signed URLs so you don't have to upload to a middle server and then S3. You can now create a signed PUT operation that you can upload directly to S3.
  • Encoding of signed URLs to avoid issues with weird filenames
  • Preserve content type on copy
  • Ability to choose how many times to retry s3 operations when they fail with a 500 or 503. This can happen due to throttling or rate limiting. You can configure it with the new setting: retriesOnError and it defaults to 3.
  • New ColdBox Module template
  • Add bucket name to test suite
  • Github actions migration
  • Avoid error logs for objectExists()

Fixed

  • @bdw429s Fixed tons of issues with filename encodings. :party:
  • 404 is not an "error" status when verifying for errors on requests
  • The argument name in putObject() was incorrect "arguments.content" instead of "arguments.data", this only happens when md5 == "auto" so it probably slipped by for some time.

v4.8.0 => 2021-JUL-06

Added

  • Migrations to github actions
  • Added new argument to downloadObject( getAsBinary : 'no' ) so you can get binary or non binary objects. Defaults to non binary.

v4.7.0 => 2021-MAR-24

Added

  • Adobe 2021 to the testing matrix and supported engines

Fixed

  • Adobe 2021 issues with date formatting
  • Watcher needed to use the root .cfformat.json

v4.6.0 => 2021-FEB-18

Added

  • New method: setAccessControlPolicy() so you can add ACLs to buckets
  • getBucket() has been updated to use the ListObjectsv2 API - which is recommended by AWS for more detailed information.
  • Implements SigV4-signed requests thanks to @sbleon's amazing work!
  • Added more formatting rules via cfformat
  • Added a gitattributes for cross OS compatibilities
  • Added a markdownlint.json for more control over markdown
  • Added new package script : format:watch to format and watch :)

Changed

  • Updated tests to fire up in ColdBox 6
  • Handles some cleanup of parameters which were being passed as resource strings ( which were then being encoded and blowing up ).
  • Updated release recipe to match newer modules.

Removed

  • Cleanup of old cfml engine files
  • Cleanup of old init code
  • Removed some settings from test harness

v4.5.0 => 2020-MAR-11

  • Feature : SV4Util is now a singleton for added performance and more configuration expansion by adding the sdk reference
  • Improvement : Better error messages when s3 goes 💥
  • Bug : Fix for ACF double encoding

v4.4.0 => 2019-MAY-15

  • Reworked SSL setup to allow for dynamic creation of the URL entry point
  • Removed ACF11 officially, it is impossible to deal with their cfhttp junk! It works, but at your own risk.

v4.3.0 => 2019-APR-05

  • Removal of debugging code

v4.2.1 => 2019-MAR-26

  • Avoid double encoding on copy, putObjectFile, and delete() operations
  • Consolidate ssl to use variables instead of arguments

v4.2.0 => 2019-MAR-15

  • ACF compatiblities
  • Fixes for auth on folder commands
  • New constructor args: defaultDelimiter for folder operations, defaultBucketname so you can set a default bucket for all bucket related operations.
  • Avoid nasty error on bucket deletion
  • Add new method objectExists() boolean check for objects
  • Fix URI encoding on signatures for headers and query params

v4.1.1 => 2019-MAR-26

  • Left some dump/aborts

v4.1.0 => 2019-MAR-13

  • DigitalOcean Spaces compatiblity
  • Region naming support, you can now pass the awsRegion argument to the constructor to select the AWS or DO region
  • SSL is now the default for all operations
  • Addition of two new constructor params: awsRegion and awsDomain to support regions and multi-domains for AWS and Digital Ocean
  • Added log debugging to calls and signatures if LogBox is on debug level

v4.0.1 => 2018-OCT-22

  • Fixes to models location, oopsy!

v4.0.0 => 2018-OCT-20

  • AWS Region Support
  • Migrated Module Layout to use Ortus Standard Module Layout
  • Added testing for all ACF Engines
  • Rework as generic Box module (compatibility change), you must move your s3sdk top level settings in ColdBox Config to moduleSettings.s3sdk
  • deleteBucket() returns false if bucket doesn't exist instead of throwing an exception
  • Few optimizations and documentation of the API

v3.0.1

  • Travis Updates and self-publishing

v3.0.0

  • Ugprade to ColdBox 4 standards
  • Upgrade to latest Amazon S3 SDK standards
  • Travis build process

v2.0

  • Original Spec as a ColdBox Plugin

$ box install s3sdk

No collaborators yet.
     
5.00 / 4
  • {{ getFullDate("2009-11-25T03:34:25Z") }}
  • {{ getFullDate("2026-09-15T10:31:24Z") }}
  • 39,676
  • 341,743