S3 client

This page contains examples with the S3 client. See the client introduction for a more detailed description how to use a client. You may also want to consider the authentication documentation to understand the many ways you can authenticate with AWS.

The S3 package could be installed with Composer.

composer require async-aws/s3

A new client object may be instantiated by:

use AsyncAws\S3\S3Client; $s3 = new S3Client();

The authentication parameters is read from the environment by default. You can also specify a AWS access id and secret:

use AsyncAws\S3\S3Client; $s3 = new S3Client([ 'accessKeyId' => 'my_access_key', 'accessKeySecret' => 'my_access_secret', 'region' => 'eu-central-1', ]);

For all available options, see the configuration reference.

The client supports presign of requests to be able to pass the URL to an unauthorized party so they can download a file within the next X minutes. Read more about presign here.

Note: There is a SimpleS3Client that might be easier to work with for common use cases.


Upload files

If you want to upload a 1 Gb file, you really don't want to put that file in memory before uploading. You want to do it a smarter way. AsyncAws allow you to upload files using a string, resource, closure or a iterable. See the following examples:

use AsyncAws\S3\S3Client; $s3 = new S3Client(); // Upload plain text $s3->putObject([ 'Bucket' => 'my-company-website', 'Key' => 'robots.txt', 'Body' => "User-agent: *\nDisallow:", ]); // Upload with stream $resource = \fopen('/path/to/big/file', 'r'); $s3->putObject([ 'Bucket' => 'my-company-website', 'Key' => 'file.jpg', 'Body' => $resource, ]); // Upload with Closure $fp = \fopen('/path/to/big/file', 'r'); $s3->putObject([ 'Bucket' => 'my-company-website', 'Key' => 'file.jpg', 'ContentLength' => filesize('/path/to/big/file'), // This is important 'Body' => static function(int $length) use ($fp): string { return fread($fp, $length); }, ]); // Upload with an iterable $files = ['/path/to/file1.txt', '/path/to/file2.txt']; $s3->putObject([ 'Bucket' => 'my-company-website', 'Key' => 'file_merged.jpg', 'ContentLength' => array_sum(array_map('filesize', $files)), // This is important 'Body' => (static function() use($files): iterable { foreach ($files as $file) { yield file_get_contents($file); } })(), ]);

When using a Closure, it's important to provide the property ContentLength. This information is required by AWS, and cannot be guessed by AsyncAws. If ContentLength is absent, AsyncAws will read the output before sending the request which could have a performance impact.

Download files

When you download a file from S3, AsyncAws gives you a ResultStream which can be used as a string, as a resource, or iterated over. This allows you to handle larger files without having them in memory.

// download a file and use it directly as string $result = $s3->getObject([ 'Bucket' => 'my-company-website', 'Key' => 'metadata.json', ]); $metadata = json_decode($result->getBody()->getContentAsString()); // download a big file and save it efficiently $result = $s3->getObject([ 'Bucket' => 'my-company-website', 'Key' => 'bunny.mkv', ]); $fp = fopen('/path/to/big_file.mkv', 'wb'); stream_copy_to_stream($result->getBody()->getContentAsResource(), $fp); // use an iterable to perform some business logic on chunks while downloading (or show a progress bar) $result = $s3->getObject([ 'Bucket' => 'my-company-website', 'Key' => 'orders.csv', ]); $fp = fopen('/path/to/orders.csv', 'wb'); foreach ($result->getBody()->getChunks() as $chunk) { fwrite($fp, $chunk); $progress->advance(); }

Add tags to a bucket

You can add tags to your buckets to help you find related resources in the AWS cost explorer; eg all AWS resources tagged with environment = staging would show you the amount you're spending on your pre-prod environment each month

$client->putBucketTagging( new PutBucketTaggingRequest([ 'Bucket' => 'my-website-assets-bucket', 'Tagging' => new Tagging([ 'TagSet' => [ new Tag([ 'Key' => 'environment', 'Value' => 'production', ]), new Tag([ 'Key' => 'project-name', 'Value' => 'unicorn', ]) ], ]), ]) );

Managing object tags

You can associate multiple key-value pairs (tags) with each of your S3 objects, with the ability to change them at any time. The tags can be used to manage and control access, set up lifecycle rules, customize S3 Storage Class Analysis, and filter CloudWatch metrics.

Learn more at Simplify your data lifecycle by using object tags with Amazon S3 Lifecycle

$client->putObjectTagging( new PutObjectTaggingRequest([ 'Bucket' => 'examplebucket', 'Key' => 'baz/HappyFace.jpg', 'Tagging' => new Tagging([ 'TagSet' => [ new Tag([ 'Key' => 'expire-after-30-days', 'Value' => '1', ]) ], ]), ]) ); $client->getObjectTagging( new GetObjectTaggingRequest([ 'Bucket' => 'examplebucket', 'Key' => 'baz/HappyFace.jpg', ]) ); $client->deleteObjectTagging( new DeleteObjectTaggingRequest([ 'Bucket' => 'examplebucket', 'Key' => 'baz/HappyFace.jpg', ]) );

Virtual Hosted-Style Requests

When calling AWS endpoints, AsyncAws uses Virtual Hosted-Style Requests: The bucket name is part of the endpoint's host. To change this behavior, and use "path styled endpoints" instead, set pathStyleEndpoint parameter to true when initializing the client.

use AsyncAws\S3\S3Client; $s3 = new S3Client(['pathStyleEndpoint' => true]);

Chunked body

When sending data to AWS endpoints, AsyncAws split the content in multiple chunks. This improves UX by avoiding reading the file twice (required to compute the signature ) which could be a performance issue when file is really big, or the uploaded content is not a file (ie. streamed from an HTTP request). But some 3rd party services like Openstack Swift, pretending being "S3-compatible" does not support chunked body. To change this behavior, set sendChunkedBody parameter to false when initializing the client.

use AsyncAws\S3\S3Client; $s3 = new S3Client(['sendChunkedBody' => false]);

Non-AWS S3 endpoints

To use the S3Client with example Digital Oceans' Spaces, you need to initialize the S3Client with your endpoint.

$s3 = new S3Client([ 'endpoint' => 'https://fra1.digitaloceanspaces.com', 'pathStyleEndpoint' => true, ]);

The source code to this page is found on GitHub.