Overview
This Laravel package contains additional functionality not currently in Laravel for interfacing with Amazon’s S3 service. In particular, there are methods for dealing with versioned objects within S3. It simply extends the existing core classes to add support for versioning in S3, and is tied into the Storage
facade for convenience. It was designed to be a drop-in replacement, and is backwards compatible with the core functionality, so there shouldn’t be any conflicts. I developed this package originally for my own need to deal with versioned objects in S3 and wanted the convenience of Laravel’s Storage
facade.
With this package, you can easily:
- Manage versioned objects stored in S3
- Get a list of versions for a given object stored in S3
- Retrieve a specific version of an object stored in S3
- Delete a specific version of an object stored in S3
- Set or clear Amazon S3/API option values
- Execute other Amazon S3/API commands against your objects
Other methods and conveniences may be added in the future, depending largely upon either my own needs, or suggestions from the community. Pull requests, bug reports, etc. are welcome! 🙂
NOTE: Yes, I know that you can make use of the underlying Amazon S3 API package to do these sorts of things. But I wanted the convenience of tying them into the Storage
facade, as well as for some potential additional functionality down the road. So, if you’d rather do this:
// Instantiate an Amazon S3 client. $s3 = new S3Client([ 'version' => 'latest', 'region' => 'us-west-2' ]); // Fetch the latest version of a file try { $s3->putObject([ 'Bucket' => 'my-bucket', 'Key' => 'myfile.png', 'VersionId' => 'fiWFsPPFwbvlGh37rB9IaZYkO4pzOgWGz' ]); } catch (Aws\S3\Exception\S3Exception $e) { echo "There was an error retrieving the file.\n"; }
… instead of this:
$file = Storage::disk('s3-tools')->getVersion($versionId)->get('myfile.png');
… then that’s on you. Have fun. 🙂
Requirements
This package assumes you have already installed the following packages:
Laravel should already have the league/flysystem
package installed, but you may need to install the others. I’ve added them as dependencies to this package, so it should be all automatic for you anyway.
Installation
You can install the package via composer:
composer require incursus/laravel-s3-tools
Once it is installed, you will need to add the service provider, as usual, to your config/app.php
file:
... 'providers' => [ ... Incursus\LaravelS3Tools\S3ToolsServiceProvider::class, ... ], ...
Configuration
Environment Variables
AWS Environment Variables
The laravel-s3-tools
package makes use of the existing AWS/S3 configuration within Laravel, so if you’ve already configured your app to use S3, you are good to go! Of course, provided you are using the most recent AWS/S3 config statements (these were changed not too long ago in Laravel). To make sure, check your .env
file for the following:
AWS_ACCESS_KEY_ID=<YOUR KEY> AWS_SECRET_ACCESS_KEY=<YOUR SECRET> AWS_DEFAULT_REGION=<DEFAULT REGION> AWS_BUCKET=<YOUR BUCKET NAME>
If you aren’t sure what value to use in AWS_DEFAULT_REGION
, check this page for more information (use the value shown in the Region
column in the table on that page.
S3 Tools Disk Name
By default, this package will use a disk name of s3-tools
. If you’d like to rename it to something else, you can use the S3_TOOLS_DISK_NAME
environment variable in your .env
file, as show below.
S3_TOOLS_DISK_NAME="diskname"
Disk Configuration
The laravel-s3-tools
package requires that you setup a new disk configuration in your config/filesystems.php
file. It’s pretty simple, really. Just copy the entry below and paste it into your config/filesystems.php
file. It will automatically look in your .env
file for a custom disk name, and if not found, will fall back to the default value of simply s3-tools
. This disk name will be the disk you use in the Storage
facade whenever you want to utilize the functionality of this package. Th new disk configuration can also be used for normal, non-versioned S3 operations, or you can just use the original ‘s3’ configuration for that. Up to you!
So, your config/filesystems.php
file should look something like this:
<?php return [ ... 'disks' => [ ... 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), ], // Add this entry env('S3_TOOLS_DISK_NAME', 's3-tools') => [ 'driver' => env('S3_TOOLS_DISK_NAME', 's3-tools'), 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), ], ], ...
Usage
Summary of Methods
This it the TL;DR
section. The following are the methods available to you with the laravel-s3-tools
package. Each is described in more detail, with examples, below:
Method Name | Arguments | Description | ||
---|---|---|---|---|
setOption() |
$optionName, $optionValue | Sets the value of a single AWS/S3 API option | ||
setOptions() |
$optionArray | Sets multiple AWS/S3 API option values | ||
clearOption() |
$optionName | Resets/clears a single AWS/S3 API option | ||
clearOptions() |
N/A | Resets/clears all AWS/S3 API options that you’ve set through either setOption() or setOptions() |
||
getObjectVersions() |
$objectPath | Fetches a list of versions of the specified object stored in S3 | ||
getVersion() |
$versionId | Shortcut for setOption('VersionId', $versionString) |
||
has() |
$objectPath | Works the same as the normal has() method in Laravel, but provides support for checking for existence of a specific version of an object. |
||
delete() |
$objectPath | Works the same as the normal delete() method in Laravel, but provides support for deleting a specific version of an object. |
Get a list of versions for a given object
A list of available versions of an object (file) stored in S3 can be retrieved and processed as follows. The returned list of versions will appear in reverse-chronological order based on the date last modified. The most recent version (the latest version) will always be the first element (0th) in the returned array.
$versions = Storage::disk('s3-tools')->getVersions('myfile.png'); foreach($versions as $v) { echo '<li> Version ID: ' . $v['versionId']; echo '<li> File Size: ' . $v['fileSize'] . ' bytes'; echo '<li> Is Latest Version?: ' . $v['isLatest']; // Will be true or false (boolean) echo '<li> Date Modified: ' . $v['dateModified']; echo '<li> ----------------------------------------------'; }
The output from the above code will appear similar to the following:
- Version ID: WX6q0O9qkcAcqld3DidZo2m5z68uGKnn - File Size: 132645 bytes - Is Latest Version?: 1 - Date Modified: 2019-03-21T19:35:29+00:00 ---------------------------------------------- - Version ID: nMw5IAmOPdMK0MR3eXtkSPVQTd18Vucd - File Size: 3631 bytes - Is Latest Version?: - Date Modified: 2019-03-21T19:16:26+00:00 ---------------------------------------------- ...
Retrieve the latest version of an object
To retrieve the latest version of the a given object, simply use the Storage
facade as usual. Here is an example of retrieving the latest version of an image from S3.
// Fetch the latest version of the file from S3 $file = Storage::disk('s3-tools')->get('myfile.png'); // Show the image in the browser return response($file)->header('Content-Type', 'image/png');
Fetch a specific version of an object
However, unlike Laravel, it can also be used to specify a specific version of an object that you wish to retrieve. The versionId
field returned by getVersions()
can be used to retrieve a specific version of an object from S3:
// Fetch the image from S3 $versionId = 'fiWFsPPFwbvlGh37rB9IaZYkO4pzOgWGz'; $file = Storage::disk('s3-tools')->getVersion($versionId)->get('myfile.png'); // Show the image in the browser return response($file)->header('Content-Type', 'image/png');
Delete the latest version of an Object
Without specifying a specific version, the latest version of an Object will be deleted:
$result = Storage::disk('s3-tools')->delete('some/longer/S3/path/business-plan.pdf');
The above operation will actually not "delete" the file from S3 if versioning is enabled for the bucket. By default, S3 will place a DeleteMarker
for that version of the file. However, you are charged a nominal fee by Amazon for DeleteMarker
storage. To fully delete a file, and leave no DeleteMarker
in its place, you need to delete the specific version of the file as demonstrated below.
Alternatively, you can do the following to help manage your DeleteMarkers
in S3:
- Login to the S3 Console
- Select your Bucket
- Open Properties
- Click Lifecycle
- Create a rule set to Permanently Delete
n
days after the object’s creation date
Delete a specific version of an Object
If you specify a versionId
, you can delete just that particular version of the object, assuming it exists. This operation will also not leave behind a DeleteMarker
– think of it as a "hard delete" operation.
$versionId = 'fiWFsPPFwbvlGh37rB9IaZYkO4pzOgWGz'; $result = Storage::disk('s3-tools')->getVersion($versionId)->delete('some/longer/S3/path/business-plan.pdf');
Setting AWS/S3 API Options
At times, you may need to provide additional options for a given request. The options for each API call are well-documented on Amazon’s API Reference site. As an example, consider this request which does the same thing as the built-in getVersion()
method in this package:
$result = Storage::disk('s3-tools')->setOption('VersionId', $versionString)->get('myfile.png');
You can also use the plural version called setOptions()
to pass in an array of options:
$options = [ 'VersionId' => 'fiWFsPPFwbvlGh37rB9IaZYkO4pzOgWGz', 'IfModifiedSince' => '2 days ago' ]; $result = Storage::disk('s3-tools')->setOptions($options)->delete('myfile.png');
The clearOption()
method will reset a specific option, while the clearOptions()
method will reset them all. If you experience any weirdness while doing complex operations into and out of S3, it may behoove you call clearOptions()
to reset things prior to making certain API calls.
// Retrieve a specific version of a file $versionId = 'fiWFsPPFwbvlGh37rB9IaZYkO4pzOgWGz'; $file = Storage::disk('s3-tools')->setOption('VersionId', $versionId)->get('myfile.png'); // Clear out ll of our options $file = Storage::disk('s3-tools')->clearOptions(); // or alternatively, just clear the 'VersionId' option //$file = Storage::disk('s3-tools')->clearOption('VersionId'); // Get the latest version of another file ... $file = Storage::disk('s3-tools')->get('myfile.png');
Execute Other Amazon S3 API Commands
Using the command()
method, you can execute any other API call to S3 as well, and there are a great number of them. However, you will be responsible for not only passing in all of the appropriate options, but also parsing the response. All responses returned via this method are sent back to you in raw format. In some senses, this is a bit extraneous, since you could just use the offical S3 API to execute them, but I’ve included it here just to provide a method of consistency should you decide to use this package for other things.
Consider the following example which does the same thing as the built-in getVersions()
method of this package:
$result = Storage::disk('s3-tools')->command('ListObjectVersions', [ 'Prefix' => 'some/longer/S3/path/business-plan.pdf' ]);
Here is the same command above, but using a different bucket name:
$result = Storage::disk('s3-tools')->command('ListObjectVersions', [ 'Bucket' => 'MyBucketName', 'Prefix' => 'some/longer/S3/path/business-plan.pdf' ]);
Here is an example of creating a new S3 bucket. Remember, bucket names in S3 must conform to DNS naming conventions, so:
- Should not contain uppercase characters
- Should not contain underscores
- Should be between 3 and 63 characters long
- Should not end with a dash
- Cannot contain two, adjacent periods
- Cannot contain dashes next to periods (e.g., "my-.bucket.com" and "my.-bucket" are invalid)
$result = Storage::disk('s3-tools')->command('CreateBucket', [ 'Bucket' => 'my-terrific-bucket-name', 'ACL' => 'private' ]);
Here is a final example for you. Removing multiple objects in a single API call. In this example, we delete the latest version of myfile.png
and business-plan.pdf
, as well as a specific version of a fictitious spreadsheet.
$result = Storage::disk('s3-tools')->command('DeleteObjects', [ 'Delete' => [ [ 'Key' => 'myfile.png' ], [ 'Key' => 'some/longer/S3/path/business-plan.pdf' ], [ 'Key' => 'some/longer/S3/path/financial-planning-spreadsheet.xlsx', 'VersionId' => 'fiWFsPPFwbvlGh37rB9IaZYkO4pzOgWGz' ] ] ]);
Notes on Storage::command
Usage
- When using the
Storage::command
method, the only "option" value that WILL actually default isBucket
… it will default to the value ofAWS_BUCKET
from your.env
file if a bucket name isn’t passed in directly.
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security
If you discover any security related issues, please email scott@incurs.us instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.