Symfony. Subiendo archivos con Flysystem a S3
Introducción a la librería Flysystem y cómo configurarla para subir archivos a cualquier sistema de almacenamiento: S3, Azure…
Hoy os quiero hablar del bundle OneupFlysystemBundle, que nos permite integrar la fantástica librería Flysystem del equipo de desarrolladores “The PHP league” en nuestros proyectos de Symfony.
Para los que no conozcáis esta librería, lo que nos permite en abstraer el sistema de almacenamiento de los documentos de nuestra aplicación, pudiendo almacenarlos donde queramos sin necesidad de preocuparnos de cómo conectarnos a las distintas soluciones como S3 o Blob Storage de Azure.
Fue hace unos meses cuando descubrí esta librería y quería compartirla con todos vosotros ya que para mí se va a convertir en un bundle imprescindible dentro de los proyectos que así lo requieran.
Vamos a ver cómo configurarla!
0. Instalación y configuración de AWS SDK PHP
Este tutorial sobre cómo configurar OneupFlysystemBundle en Symfony tiene como objetivo poder subir nuestros archivos a AWS S3. Si empleáis otro servicio veréis como la adaptación es tremendamente sencilla y tan sólo implica un par de cambios por lo que seguro que también os sirve.
Puesto que vamos a subir los archivos a S3, lo primero que necesitaremos es instalar el SDK de AWS lo cual haremos mediante composer:
composer require aws/aws-sdk-php
Dado que posteriormente necesitaremos pasar el cliente del sistema de almacenamiento que estamos empleando a la configuración de OneUP Flysystem , declararemos la clase Aws\S3\Client
como si fuera un servicio dentro de nuestro archivo services.yaml
:
Aws\S3\S3Client: arguments: - version: 'latest' region: '%env(AWS_S3_BUCKET_REGION)%' credentials: key: '%env(AWS_S3_ACCESS_ID)%' secret: '%env(AWS_S3_ACCESS_SECRET)%'
Además, haremos configurables como variables de entorno sus principales argumentos: la región del sistema así como su las credenciales de acceso del usuario IAM con permisos para acceder al bucket de S3 que estaremos empleando.
Por tanto, no os olvidéis de crear estas variables en vuestro archivo .env
y adaptarlas para vuestro entorno local en .env.local
.
1. Instalación de OneupFlysystemBundle
El siguiente paso es instalar el bundle OneupFlysystemBundle lo cual nos traerá como dependencia la librería Flysystem.
composer require oneup/flysystem-bundle
Tras asegurarnos que el bundle se encuentra registrado dentro de nuestro archivo bundles.php
crearemos sus archivo de configuración en config/packages/oneup_flysystem.yaml
con el siguiente contenido:
oneup_flysystem:
adapters:
my_adapter:
local:
directory: "%kernel.root_dir%/cache"
filesystems:
my_filesystem:
adapter: my_adapter
visibility: private
Como podéis ver, el bundle funciona en base a dos conceptos:
- Los adapters, que contienen la integración con el sistema de archivos que queramos emplear
- Los filesystems, de los cuales podemos crear tantos como queramos y que proporcionan los servicios necesarios a nuestra aplicación para poder trabajar con archivos abstrayendo la capa de conexión con el sistema empleado gracias al adapter.
La configuración si estuviéramos trabajando con el sistema local de archivos sería la que os acabo de mostrar, pero puesto que la idea es trabajar con S3 vamos a modificarlo un poco para emplear este servicio.
Configurando S3 con OneupFlysystemBundle de Symfony
La librería Flysystem de PHP League nos permite integrarnos con los distintos servicios de almacenamiento por medio de adaptadores.
En nuestro caso, instalaremos el correspondiente a S3:
composer require league/flysystem-aws-s3-v3
el cual actúa de conector entre la librería Flysystem y el cliente de S3 que se encuentra dentro del SDK de PHP de AWS que instalamos en el paso previo.
Instalado el adaptador, modificaremos nuestro archivo config/packages/oneup_flysystem.yaml
para que lo emplee:
oneup_flysystem: adapters: default_adapter: awss3v3: client: Aws\S3\S3Client bucket: '%env(AWS_S3_BUCKET_NAME)%' filesystems: default_filesystem: adapter: default_adapter
Gracias que configuramos el cliente de S3 como servicio, podemos directamente emplearlo dentro de la propiedad client
del adaptador de s3, proporcionando además el nombre del bucket que usará nuestra aplicación.
Finalmente, modificaremos el único filesystem que tendrá nuestra aplicación para emplear el adapter definido.
Con esto ya tenemos configurado el bundle y podemos pasar a subir ficheros. Vamos a verlo!
2. Subiendo archivos a S3
De cara simplificar el artículo supondremos que sólo tenemos una entidad Article
(como si de un blog se tratara) y que por tanto todas las imágenes van a ir a la misma carpeta dentro del bucket. Esto nos ahorrará tener que crear una capa dentro de nuestra aplicación que sirva para escoger la carpeta destino en función de la entidad.
La subida a archivos la haremos por medio de un servicio que crearemos llamado FileUploader y que recibirá como argumentos el servicio default_filesystem
que declaramos en el punto anterior.

El proceso es bastante similar al que sugiere Symfony en su propia documentación:
https://symfony.com/doc/current/controller/upload_file.html
- En las líneas 27 y 28 crearemos un nombre de fichero “seguro” generando para ello un slug a partir del nombre así cómo un id único para el mismo.
- En la línea 30 definiremos el directorio destino (sí, lo he hardcodeado para este artículo 🙀 🙀 🙀)
- En la línea 31 abriremos un stream mediante la función
fopen
apuntando al path donde se encuentra el archivo subido. - Y en la línea 32 emplearemos el servicio
filesystem
que inyectamos en nuestro servicio para subir el archivo empleando el stream abierto. - Finalmente cerraremos el stream en la línea 43 de cara a evitar memory leaks.
Fácil, ¿verdad? Casi. Si os dais cuenta, en el constructor de nuestro servicio estamos inyectando la interfaz FilesystemInterface
, pero dado que la librería nos permite tener tantos file systems como queramos, es necesario que definamos el valor de esa interfaz en nuestra configuración.
Por tanto, iremos a nuestro archivo services.yaml
y añadiremos lo siguiente:
services:
League\Flysystem\FilesystemInterface: '@oneup_flysystem.default_filesystem_filesystem'
Es cierto que también podriamos haber bindeado manualmente el argumento en la configuración del servicio, pero si sólo vais a emplear un file system veo más cómoda esta configuración ya que nos permite especificar la interfaz FilesystemInterface
en el constructor de nuestros servicios y que Symfony la inyecte directamente.
3. Mostrando nuestros archivos de S3
Bien, ya tenemos nuestros archivos subidos. ¿Cómo podemos mostrarlos dentro de Twig o devolverlos con la URL completa en el caso de que estemos montando una API?
Para ilustrar esto añadiré un método dentro de nuestra clase FileUploader
llamado getPublicPath
que nos permitirá generar las URL’s de nuestros archivos subidos en S3.
Idealmente este método sería conveniente sacarlo a otro servicio pero nuevamente no quiero complicar mucho el tutorial. Así que vamos a ver cómo quedaría.

El método getPublicPath
es muy sencillo. Recibe el nombre del fichero a mostrar, al cual le concatenamos tanto el directorio que hemos hardcodeado malamente como la URL donde se encuentran nuestros assets.
Esta URL la he decidido pasar como argumento del constructor del servicio ya que puede cambiar en función del entorno de desarrollo, permitiéndonos mayor flexibilidad así como realizar los tests correspondientes.
Por tanto, en nuestro archivo services.yaml
definiremos el siguiente parámetro:
parameters:
app.uploads_base_url: 'https://%env(AWS_S3_BUCKET_NAME)%.s3.amazonaws.com'
Y lo emplearemos para definir el valor del argumento $publicAssetBaseUrl
de nuestro servicio:
App\Service\Utils\FileUploader: arguments: $publicAssetBaseUrl: '%app.uploads_base_url%'
Con esto ya podemos emplear el método tanto en una extensión de Twig como en un normalizador del componente Symfony Serializer para obtener la URL completa al archivo.
4. Borrando archivos de S3
Si subir archivos a S3 es sencillo gracias a OneUpFileSystemBundle, eliminarlos es una sola línea:
$this->filesystem->delete($path);
Donde $path
es el path a nuestro archivo (no la URL completa) dentro de nuestro bucket de S3.
Conclusiones
Como habéis podido ver, gracias a este bundle y la librería en la que se basa gestionar nuestros ficheros es algo que pasa a ser muy muy sencillo y 100% configurable sin necesidad de bajar al desarrollo de los distintos adaptadores, ya que PHP League nos los proporciona como librerías independientes por medio de composer.
Tal y como os decía al comienzo del artículo para mí ha sido un descubrimiento y sobre todo me ha gustado la gran flexibilidad que aporta a la hora de gestionar los ficheros por lo que os animo a profundizar más ya que existen numerosas cosas que se pueden hacer gracias a esta librería.
Apóyame en Patreon
Gracias a: Alex Fuentes y Jorge Hernández.
¿Quieres recibir más artículos como este?
Si te ha gustado este artículo te animo a que te suscribas a la newsletter que envío cada domingo con publicaciones similares a esta y más contenido recomendado: 👇👇👇