How to upload files to AWS S3 using Symfony
I’m migrating a legacy PHP application to Symfony, and that includes implementing file uploads to AWS S3. Symfony does not provide this feature out of the box, and many of the existing tutorials are outdated or do not work well, therefore I decided to document this in the hope that it will be useful to someone else.
To implement this, we will need to install four packages:
aws/aws-sdk-php
: the AWS PHP SDK.oneup/flysystem-bundle
: a bundle for Flysystem (league/flysystem
), a filesystem abstraction library.league/flysystem-aws-s3-v3
: a Flysystem adapter for AWS SDK.vich/uploader-bundle
: a bundle that eases file uploads that are attached to ORM entities.
These packages can be easily installed with Composer:
Next, we need to add to AppKernel.php
our newly installed bundles:
Now, we will start setting up the bundles on the configuration file app/config.yml
.
First, we will setup the Flysystem bundle. This bundle will expose a set of adapters and filesystems for the application to use. The configuration below creates an adapter called assets_adapter
and a filesystem called assets_fs
, which in turn uses the adapter assets_adapter
. The built-in awss3v3
Flysystem adapter takes two arguments: a service that’s nothing more than an instance of the AWS S3 SDK and the AWS bucket name (in this case, from the parameters.yml
variable called assets_bucket
).
Next, we will setup the VichUploaderBundle
. The configuration below tells VichUploaderBundle
to use the orm
driver, which integrates with Doctrine, and to use the Flysystem storage adapter. It also creates a mapping, which will be hooked into our entity. A mapping tells VichUploaderBundle
where to upload a file, how to generate its URI, and what naming strategy to use. In this case, we are supplying a user defined URI prefix, which is the S3 bucket URI (it’s defined in parameters.yml
); we are telling it to upload the file the the assets_fs
filesystem; and we are telling it to use the naming strategy that generates unique IDs for each file.
Finally, on app/services.yml
, we will create the AWS S3 service that will be used to upload the files to S3. The configuration below defines a service named app.assets.s3
that is an instance of the Aws\S3\S3Client
class, which takes the arguments defined below. These arguments are best stored in the parameters.yml
file.
Good! Now it’s time to plug everything into our model.
Let us suppose we have an entity called Product
, which represents a… product, ta-da! First, we will start by adding the @Vich\Uploadable
annotation to the class, which allows it to upload the files. Then, we will add two properties: $image
, which represents an image that was uploaded, and $imageFile
, which represents a image that may be uploaded. Finally, we will also add a few setters and getters. The complete class can be seen below:
Note that the $imageFile
property has the following annotation:
This annotation tells VichUploaderBundle
to upload the file using the assets
mapping we defined earlier, and to use the $image
property to hold the details of the uploaded file.
Now, how do we upload an image? We add an image field to the product creation form. VichUploaderBundle
provides a special form type VichImageType
that automatically handles the file download, file preview and deletion. We simply add it to our form and use the $imageFile
property - remember, this is the property that handles the image upload.
What about our controller? What do we need to change? Fortunately, we don’t need to change anything: VichUploaderBundle
is well implemented, and uploading is done behind the scenes when the forms are processed.
That’s all. You’re ready to start uploading. ;)
Notes
You probably do not want to upload your files to S3 on your development and test environments (maybe you do, but that’s not very smart, lol). You can get over this by using a local filesystem on dev and a in-memory filesystem on test.
I used to have Disqus enabled on my website, but I have disabled it because of privacy concerns.
If you feel like commenting or asking something, you can still contact me via other means.