Symfony2: Configuring VichUploaderBundle and Gaufrette to use AmazonS3

Last week, I was looking to install the VichUploaderBundle into a Symfony2 project to automatically handle file uploads. As I was looking through the Vich documentation I ran across a chunk describing being able to use Gaufrette to skip the local filesystem and push files directly to Amazon S3. Since we’d eventually need to load balance the app and push uploaded files to S3 anyway, I decided to set it up out of the gate. Unfortunately, the documentation for setting up Vich with Gaufrette is a bit opaque so here’s a step by step guide to getting it going.

Install Everything

The first thing you’ll want to do is install all the required packages. If you’re using Composer, the following will work:

ashish@ashish:~/composer require amazonwebservices/aws-sdk-for-php:dev-master
ashish@ashish:~/composer require KnpLabs/Gaufrette:dev-master
ashish@ashish:~/composer require knplabs/knp-gaufrette-bundle:dev-master
ashish@ashish:~/composer require vich/uploader-bundle:dev-master

Once all the packages are installed, you’ll need to configure *both* Gaufrette and Vich. This is where the documentation broke down a bit for me. You’ll need your Amazon AWS “Access Key ID” and “Secret Key” which are both available at https://portal.aws.amazon.com/gp/aws/securityCredentials if you’re logged into AWS.

Configure It

# app/config/parameters.yml
parameters:
...
aws_key: "YOUR_AWS_KEY"
aws_secret_key: "YOUR_AWS_SECRET"
# app/config/services.yml
# These are pulling from parameters.yml but you could hardcode them in
services:
...
ct_file_store.s3:
class: AmazonS3
arguments:
options: { key: %aws_key%, secret: %aws_secret_key% }
# app/config/config.yml
knp_gaufrette:
stream_wrapper: ~
adapters:
uploads:
amazon_s3:
amazon_s3_id: ct_file_store.s3 #this needs to match the "service" you defined above
bucket_name: somebucket-name
options:
create: true
filesystems:
amazon_s3:
adapter: uploads
vich_uploader:
db_driver: orm
gaufrette: true
storage: vich_uploader.storage.gaufrette
mappings:
logo:
uri_prefix: https://s3.amazonaws.com/somebucket-name # you'll need this set to use the Vich URL generator
upload_destination: amazon_s3
namer: vich_uploader.namer_uniqid
delete_on_remove: true
delete_on_update: true

Once everything is configured at the YAML level, the final step is adding the Vich annotations to your entities.

<?php
namespace Setfive\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
use Gedmo\Mapping\Annotation as Gedmo;
// Make sure you add this
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Brand
*
* @ORM\Table(name="brand")
* @ORM\Entity
* @Vich\Uploadable
*
* Make sure you add this annotation or Vich fails silently.
*/
class Brand
{
/**
* @var string
*
* @ORM\Column(name="logo", type="string", length=255, nullable=true)
*/
private $logo;
/**
* @Assert\File(
* maxSize="5M",
* mimeTypes={"image/png", "image/jpeg", "image/pjpeg"}
* )
* @Vich\UploadableField(mapping="logo", fileNameProperty="logo")
*
* @var File $logo_virtual
*
* This is the virtual field that will populate logo with the resulting file.
*/
protected $logo_virtual;
...
}

Make sure you add the “@Vich\Uploadable” annotation to your Entity or Vich will fail silently.

The “mapping” specified in “@Vich\UploadableField(mapping=”logo”, fileNameProperty=”logo”)” needs to match the value under “vich_uploader.mappings” which you defined in config.yml

Finally, one last “gotcha” to be cognizant of is this bug – https://github.com/dustin10/VichUploaderBundle/issues/123. Since Vich uses Doctrine lifecycle callbacks to manage files, if no Doctrine fields are changed then the Vich code isn’t executed. The easiest way to get around this (and what we used), is just to manually update the “updated_at” column every time a form is submitted to ensure that the upload handling code is executed.

Anyway, as always, questions and comments are welcome.