Amazon S3
The S3 storage saves a copy of each file on Amazon S3. It works alongside the local files and the database storage. It needs an S3 client plugin.
Installing an S3 Client
Filebinder works with two S3 clients:
- AsyncAws, through pieceofcake2/async-aws
- the AWS SDK for PHP, through pieceofcake2/aws
Install and load one of them.
AsyncAws
Install pieceofcake2/async-aws with async-aws/simple-s3:
composer require pieceofcake2/async-aws async-aws/simple-s3Load the plugin in config/bootstrap.php:
CakePlugin::load('AsyncAws');AWS SDK for PHP
Install pieceofcake2/aws:
composer require pieceofcake2/awsLoad the plugin in config/bootstrap.php:
CakePlugin::load('Aws');Choosing the Client
Filebinder uses the client of the loaded plugin. When both plugins are loaded, it uses AsyncAws.
To choose the client yourself, set Filebinder.S3.client to 'AsyncAws' or 'Aws':
Configure::write('Filebinder.S3.client', 'AsyncAws');The plugin of the client must be loaded. Installing the AWS SDK for PHP alone is not enough. When Filebinder cannot use any client, saving a file throws a FilebinderS3StorageException. Its message says which package to install and load.
Saving Files on S3
Add BindableBehavior::STORAGE_S3 to the storage setting, and set the bucket of the field:
public $actsAs = [
'Filebinder.Bindable' => [
'storage' => [BindableBehavior::STORAGE_DB, BindableBehavior::STORAGE_S3],
],
];
public $bindFields = [
[
'field' => 'image',
'filePath' => '/var/www/html/myapp/webroot/files/',
'bucket' => 'my-bucket',
'acl' => 'private',
],
];The behavior saves each file with this key:
<urlPrefix><model>/<model_id>/<field_name>/<file_name>When a local file does not exist, find() downloads it from S3 (see Restoring Files).
The Content-Type of a file on S3 comes from the contents of the file. Filebinder detects it with the fileinfo extension, as it does for file_content_type. It does not use the type that the browser sent or the extension of the file.
S3 Settings
Each field can set its own bucket, acl, urlPrefix and region in $bindFields (see Bound Fields). For the fields without them, Filebinder uses the Filebinder.S3 settings:
Configure::write('Filebinder.S3', [
'bucket' => 'my-bucket',
'acl' => 'private',
'urlPrefix' => 'files/',
]);| Setting | Description |
|---|---|
Filebinder.S3.client | 'AsyncAws' or 'Aws'. Default: the client of the loaded plugin. |
Filebinder.S3.bucket | Bucket, for the fields without bucket. |
Filebinder.S3.acl | Canned ACL, for the fields without acl. Default: public-read. |
Filebinder.S3.urlPrefix | Prefix of the keys, for the fields without urlPrefix. |
Filebinder.S3.region | Deprecated. Set region in the plugin configuration instead. Region, for the fields without region. It overrides the plugin configuration. |
Filebinder.S3.key, Filebinder.S3.secret | Deprecated. Set the credentials in the plugin configuration, or use the default credentials. Access key of AWS. It overrides the plugin configuration. |
Client Configuration
Filebinder creates the S3 client from the configuration of the client plugin. Set the region and the credentials there.
pieceofcake2/async-aws reads the AsyncAws configuration. When AsyncAws is not set, it reads the Aws configuration. See AsyncAws Configuration.
Configure::write('AsyncAws', [
'region' => 'ap-northeast-1',
]);pieceofcake2/aws reads the Aws configuration. See AWS Configuration.
Configure::write('Aws', [
'region' => 'ap-northeast-1',
]);The region of a field overrides the region of the configuration for all the S3 requests of that field.
Credentials
When the configuration has no credentials, the SDKs use their default credentials:
- environment variables
- the shared credentials file
- the IAM role of the EC2 instance, the ECS task or the Lambda function
Filebinder.S3.key and Filebinder.S3.secret still work and override the configuration, but they are deprecated. Move the credentials to the plugin configuration, or use the default credentials.
Public and Private Files
The ACL of a file decides how the Label helper links it. The ACL is the acl of the field, or else Filebinder.S3.acl. The default is public-read.
- With a public ACL (
public-readorpublic-read-write), the helper outputs the URL of the object. Anyone can open it. - With any other ACL, such as
private, the helper outputs a presigned URL. The URL expires afterFilebinder.expire, one minute by default.
To keep the files private, set the ACL to private:
Configure::write('Filebinder.S3.acl', 'private');S3 checks the signature of a presigned URL. The files do not go through your application. See URLs of the Files.
S3 Storage API
s3Storage() returns the S3 storage of a field. The storage is a FilebinderS3StorageInterface, configured as the behavior configures it for that field:
$storage = $this->Post->s3Storage('image');Call its methods with the bucket and the key of an object:
$this->set('imageUrl', $storage->url('my-bucket', 'files/Post/1/image/photo.jpg'));The storage has these methods:
| Method | Description |
|---|---|
put(string $bucket, string $key, string $file, array $options = []): void | Uploads a local file. Options: ACL. |
get(string $bucket, string $key): string | Returns the contents of an object. |
saveAs(string $bucket, string $key, string $file): void | Downloads an object to a local file. |
delete(string $bucket, string $key): void | Deletes an object. |
url(string $bucket, string $key): string | Returns the URL of an object, as the client builds it. The URL is not signed. |
presignedUrl(string $bucket, string $key, DateTimeImmutable $expires): string | Returns a presigned URL to download an object. The URL expires at $expires. |
Storage Factory
FilebinderS3StorageFactory creates the storages. Load it first:
App::uses('FilebinderS3StorageFactory', 'Filebinder.Storage');FilebinderS3StorageFactory::create() creates the storage of the configured client. It takes client options, such as region and credentials:
$storage = FilebinderS3StorageFactory::create(['region' => 'ap-northeast-1']);FilebinderS3StorageFactory::client() returns the name of that client, 'AsyncAws' or 'Aws':
$client = FilebinderS3StorageFactory::client();