Flysystem es una librerÃa de abstracción del sistema de ficheros que permite trabajar con almacenamiento local, Amazon S3, SFTP y otros backends con exactamente la misma interfaz. Si cambias de almacenamiento, solo cambias el adaptador.
Instalación
# Core composer require league/flysystem # Adaptador S3 (requiere aws-sdk-php) composer require league/flysystem-aws-s3-v3 # Adaptador SFTP composer require league/flysystem-sftp-v3
Adaptador local
<?php
use LeagueFlysystemFilesystem;
use LeagueFlysystemLocalLocalFilesystemAdapter;
$adapter = new LocalFilesystemAdapter('/var/www/almacenamiento');
$filesystem = new Filesystem($adapter);
// Escribir un fichero
$filesystem->write('documentos/factura.txt', 'Contenido de la factura');
// Leer
$contenido = $filesystem->read('documentos/factura.txt');
// Comprobar existencia
if ($filesystem->fileExists('documentos/factura.txt')) {
echo "El fichero existen";
}
// Eliminar
$filesystem->delete('documentos/factura.txt');
// Copiar y mover
$filesystem->copy('original.jpg', 'copia.jpg');
$filesystem->move('copia.jpg', 'nueva-ubicacion/copia.jpg');
?>
Subida de imágenes con adaptador local
<?php
// En un endpoint de subida de ficheros
function subirImagen(SplFileInfo $ficheroSubido, string $destino): string
{
$extension = strtolower($ficheroSubido->getExtension());
$nombre = bin2hex(random_bytes(16)) . '.' . $extension;
$ruta = "imagenes/usuarios/$nombre";
$stream = fopen($ficheroSubido->getPathname(), 'r');
$filesystem->writeStream($ruta, $stream);
fclose($stream);
return $ruta;
}
?>
Adaptador Amazon S3
<?php
use AwsS3S3Client;
use LeagueFlysystemAwsS3V3AwsS3V3Adapter;
$cliente = new S3Client([
'region' => 'eu-west-1',
'version' => 'latest',
'credentials' => [
'key' => getenv('AWS_ACCESS_KEY_ID'),
'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
],
]);
$adapter = new AwsS3V3Adapter($cliente, 'mi-bucket', 'prefijo/');
$filesystem = new Filesystem($adapter);
// La misma API que con el adaptador local
$filesystem->write('facturas/2024/enero.pdf', $contenidoPdf);
?>
Adaptador SFTP
<?php
use LeagueFlysystemPhpseclibV3SftpAdapter;
use LeagueFlysystemPhpseclibV3SftpConnectionProvider;
$adapter = new SftpAdapter(
SftpConnectionProvider::fromArray([
'host' => 'sftp.proveedor.com',
'username' => 'usuario',
'privateKey' => '/home/web/.ssh/id_rsa',
'port' => 22,
]),
'/var/datos/entrada' // ruta raÃz en el servidor SFTP
);
$filesystem = new Filesystem($adapter);
$ficheros = $filesystem->listContents('pedidos/')->toArray();
foreach ($ficheros as $fichero) {
if ($fichero['type'] === 'file') {
$contenido = $filesystem->read($fichero['path']);
procesarPedido($contenido);
$filesystem->move($fichero['path'], 'pedidos/procesados/' . basename($fichero['path']));
}
}
?>
Listar contenidos de un directorio
<?php
// Solo el primer nivel
$items = $filesystem->listContents('facturas/')->toArray();
// Recursivo
$items = $filesystem->listContents('facturas/', true)->toArray();
foreach ($items as $item) {
echo $item['type'] . ': ' . $item['path'] . "n";
if ($item['type'] === 'file') {
echo ' Tamaño: ' . $item['file_size'] . " bytesn";
}
}
?>
Testing con InMemoryFilesystemAdapter
<?php
use LeagueFlysystemInMemoryInMemoryFilesystemAdapter;
class SubidaImagenTest extends PHPUnitFrameworkTestCase
{
public function testSubidaCorrecta(): void
{
$adapter = new InMemoryFilesystemAdapter();
$filesystem = new Filesystem($adapter);
$servicio = new ServicioImagenes($filesystem);
$servicio->subir('foto.jpg', 'contenido de prueba');
$this->assertTrue($filesystem->fileExists('imagenes/foto.jpg'));
}
}
?>
Errores comunes
- UnableToWriteFile con S3: verifica que el bucket existe, que las credenciales tienen el permiso
s3:PutObjecty que la región es correcta. - Permisos en el adaptador local: el directorio raÃz debe ser escribible por el proceso PHP. Flysystem no crea el directorio raÃz automáticamente.
- Streams no cerrados: siempre llama a
fclose()después dewriteStream()/readStream(); dejar streams abiertos agota los descriptores de fichero.
