Introducción

Laravel proporciona una poderosa abstracción del sistema de archivos gracias al genial paquete de PHP Flysystem de Frank de Jonge. La integración de Flysystem de Laravel proporciona drivers simples de usar para trabajar con sistemas de archivos locales y Amazon S3. Aún mejor, es maravillosamente simple cambiar entre estas opciones de almacenamiento de modo que la API permanezca sin cambios para cada sistema.

Configuración

La configuración del sistema de archivos está ubicada en config/filesystems.php. Dentro de este archivo puedes configurar todos tus «discos». Cada disco representa un driver de almacenamiento y una ubicación de almacenamiento en particular. Configuraciones de ejemplo para cada driver soportado están incluidas en el archivo de configuración. Así que, modifica la configuración para reflejar tus preferencias de almacenamiento y credenciales.

Puedes configurar tantos discos como quieras e incluso tener múltiples discos que usen el mismo driver.

El disco público

El disco public está pensado para archivos que serán públicamente accesibles. Por defecto, el disco public usa el driver local y almacena estos archivos en storage/app/public. Para hacerlos accesibles desde la web, debes crear un enlace simbólico desde public/storage a storage/app/public. Esta convención mantendrá tus archivos públicamente accesibles en un directorio que puede ser fácilmente compartido a través de despliegues al usar sistemas de despligue sin tiempo de inactividad como Envoyer.

Para crear un enlace simbólico, puedes usar el comando de Artisan storage:link:

php artisan storage:link

Una vez que un archivo ha sido guardado y el enlace simbólico ha sido creado, puedes crear una URL a los archivos usando el helper asset:

echo asset('storage/file.txt');

Driver local

Al usar el driver local, todas las operaciones sobre archivos son relativas al directorio root definido en tu archivo de configuración filesystems. Por defecto, este valor es establecido al directorio storage/app. Por lo tanto, el siguiente método almacenará un archivo en storage/app/file.txt:

Storage::disk('local')->put('file.txt', 'Contents');

Permisos

La visibilidad public se traduce a 0755 para directorios y 0644 para archivos. Puedes modificar el mapeo de permisos por defecto en tu archivo de configuración filesystems:

'local' => [
    'driver' => 'local',
    'root' => storage_path('app'),
    'permissions' => [
        'file' => [
            'public' => 0664,
            'private' => 0600,
        ],
        'dir' => [
            'public' => 0775,
            'private' => 0700,
        ],
    ],
],

Prerrequisitos del driver

Paquetes de Composer

Antes de usar los drivers de SFTP o S3, necesitarás instalar el paquete apropiado mediante Composer:

  • SFTP: league/flysystem-sftp ~1.0
  • Amazon S3: league/flysystem-aws-s3-v3 ~1.0

Algo sumamente recomendable para mejorar el rendimiento es usar un adaptador de caché. Necesitarás un paquete adicional para esto:

  • CachedAdapter: league/flysystem-cached-adapter ~1.0

Configuración del driver S3

La información de configuración del driver de S3 está ubicada en tu archivo de configuración config/filesystems.php. Este archivo contiene un arreglo de configuración de ejemplo para un driver de S3. Eres libre de modificar este arreglo con tu propia configuración y credenciales de S3. Por conveniencia, estas variables de entorno coinciden con la convención de nombres usada por AWS CLI.

Configuración del driver FTP

Las integraciones de Flysystem de Laravel funcionan bien con FTP; sin embargo, una configuración de ejemplo no está incluida con el archivo de configuración por defecto del framework filesystems.php. Si necesitas configurar un sistema de archivos FTP, puedes usar la siguiente configuración de ejemplo:

'ftp' => [
    'driver' => 'ftp',
    'host' => 'ftp.example.com',
    'username' => 'your-username',
    'password' => 'your-password',

    // Optional FTP Settings...
    // 'port' => 21,
    // 'root' => '',
    // 'passive' => true,
    // 'ssl' => true,
    // 'timeout' => 30,
],

Configuración del driver SFTP

Las integraciones de Flysystem de Laravel funcionan bien con SFTP; sin embargo, una configuración de ejemplo no está incluida con el archivo de configuración por defecto del framework filesystems.php. Si necesitas configurar un sistema de archivos SFTP, puedes usar la siguiente configuración de ejemplo:

'sftp' => [
    'driver' => 'sftp',
    'host' => 'example.com',
    'username' => 'your-username',
    'password' => 'your-password',

    // Settings for SSH key based authentication...
    // 'privateKey' => '/path/to/privateKey',
    // 'password' => 'encryption-password',

    // Optional SFTP Settings...
    // 'port' => 22,
    // 'root' => '',
    // 'timeout' => 30,
],

Caché

Para habilitar la caché para un disco dado, puedes agregar una directiva cache a las opciones de configuración del disco. La opción cache debe ser un arreglo de opciones de caché que contiene un nombre de disco disk, el tiempo de expiración en segundos expire, y el prefijo prefix de la caché:

's3' => [
    'driver' => 's3',

    // Other Disk Options...

    'cache' => [
        'store' => 'memcached',
        'expire' => 600,
        'prefix' => 'cache-prefix',
    ],
],

Obteniendo instancias del disco

El facade Storage puede ser usado para interactuar con cualquiera de tus discos configurados. Por ejemplo, puedes usar el método put en el facade para almacenar un avatar en el disco por defecto. Si llamas a métodos en el facade Storage sin primero llamar al método disk, la llamada al método será automáticamente pasada al disco por defecto:

use Illuminate\Support\Facades\Storage;

Storage::put('avatars/1', $fileContents);

Si tus aplicaciones interactúan con múltiples discos, puedes usar el método disk en el facade Storage para trabajar con archivos en un disco en particular:

Storage::disk('s3')->put('avatars/1', $fileContents);

Retornando archivos

El método get puede ser usado para retornar el contenido de un archivo. La cadena sin procesar (raw string) del archivo será retornadas por el método. Recuerda, todas las rutas del archivo deben ser especificadas relativas a la ubicación «raíz» configurada por el disco:

$contents = Storage::get('file.jpg');

El método exists puede ser usado para determinar si un archivo existe en el disco:

$exists = Storage::disk('s3')->exists('file.jpg');

El método missing puede ser usado para determinar si falta un archivo en el disco:

$missing = Storage::disk('s3')->missing('file.jpg');

Descargando archivos

El método download puede ser usado para generar una respuesta que obliga al navegador del usuario a descargar el archivo al directorio dado. El método download acepta un nombre de archivo como segundo argumento del método, que determinará el nombre del archivo que será visto por el usuario que está descargando el archivo. Finalmente, puedes pasar un arreglo de encabezados HTTP como tercer argumento al método:

return Storage::download('file.jpg');

return Storage::download('file.jpg', $name, $headers);

URLs de archivos

Puedes usar el método url para obtener la URL del archivo dado. Si estás usando el driver local, esto típicamente agregará /storage a la ruta dada y retornará una URL relativa al archivo. Si estás usando el driver s3, será retornada la URL remota completamente calificada:

use Illuminate\Support\Facades\Storage;

$url = Storage::url('file.jpg');

Recuerda, si estás usando el driver local, todos los archivos que deberían ser públicamente accesibles deben ser colocados en el directorio storage/app/public. Además, debes crear un enlace simbólico a public/storage que apunte al directorio storage/app/public.

URLs temporales

Para archivos almacenados usando el driver s3, puedes crear una URL temporal a un archivo dado usando el método temporaryUrl. Este método acepta una ruta y una instancia DateTime que especifica cuándo la URL debería expirar:

$url = Storage::temporaryUrl(
    'file.jpg', now()->addMinutes(5)
);

Si necesitas especificar parámetros de petición de S3 adicionales, puedes pasar el arreglo de parámetros de petición como tercer argumento del método temporaryUrl:

$url = Storage::temporaryUrl(
    'file.jpg',
    now()->addMinutes(5),
    ['ResponseContentType' => 'application/octet-stream']
);

Personalización del host de URL local

Si te gustaría predefinir el host para archivos almacenados en un disco usando el driver local, puedes agregar una opción url al arreglo de configuración del disco:

'public' => [
    'driver' => 'local',
    'root' => storage_path('app/public'),
    'url' => env('APP_URL').'/storage',
    'visibility' => 'public',
],

Metadatos de archivos

Además de leer y agregar archivos, Laravel también puede proporcionar información sobre los archivos. Por ejemplo, el método size puede ser usado para obtener el tamaño del archivo en bytes:

use Illuminate\Support\Facades\Storage;

$size = Storage::size('file.jpg');

El método lastModified retorna la marca de tiempo de UNIX de la última vez en que el archivo fue modificado:

$time = Storage::lastModified('file.jpg');

Almacenando archivos

El método put puede ser usado para almacenar el contenido sin procesar (raw) de archivo en un disco. Puedes también pasar un resource de PHP al método put, que usará el soporte subyacente de stream de Flysystem. Recuerda, todas las rutas de archivos deben ser especificadas de forma relativa a la ubicación «raíz» configurada en el disco:

use Illuminate\Support\Facades\Storage;

Storage::put('file.jpg', $contents);

Storage::put('file.jpg', $resource);

Streaming automático

Si te gustaría que Laravel automáticamente haga streaming de un archivo dado a tu ubicación de almacenamiento, puedes usar los métodos putFile o putFileAs. Este método acepta una instancia de Illuminate\Http\File o Illuminate\Http\UploadedFile y automáticamente hará stream del archivo a la ubicación deseada:

use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;

// Automatically generate a unique ID for file name...
Storage::putFile('photos', new File('/path/to/photo'));

// Manually specify a file name...
Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

Hay algunas cosas importantes a tener en cuenta sobre el método putFile. Observa que sólo especificamos un nombre de directorio, no un nombre de archivo. Por defecto, el método putFile generará un ID único que servirá como nombre del archivo. La extensión del archivo será determinada examinando el tipo MIME del archivo. La ruta al archivo será retornada por el método putFile para que puedes almacenar la ruta, incluyendo el nombre de archivo generado, en tu base de datos.

Los métodos putFile y putFileAs también aceptan un argumento para especificar la «visibilidad» del archivo almacenado. Esto es particularmente útil si estás almacenando el archivo en disco en la nube como S3 y te gustaría que el archivo sea públicamente accesible:

Storage::putFile('photos', new File('/path/to/photo'), 'public');

Añadir al inicio o al final de un archivo

Los métodos prepend y append te permiten escribir al inicio o final de un archivo:

Storage::prepend('file.log', 'Prepended Text');

Storage::append('file.log', 'Appended Text');

Copiando y moviendo archivos

El método copy puede ser usado para copiar un archivo existente a una nueva ubicación en el disco, mientras que el método move puede ser usado para renombrar o mover un archivo existente a una nueva ubicación:

Storage::copy('old/file.jpg', 'new/file.jpg');

Storage::move('old/file.jpg', 'new/file.jpg');

Carga de archivos

En las aplicaciones web, una de los casos de uso más comunes para almacenar archivos es almacenar archivos cargados por los usuarios como imágenes de perfil, fotos y documentos. Laravel hace que sea muy fácil almacenar archivos cargados usando el método store en la instancia de un archivo cargado. Llama al método store con la ruta en la quieres almacenar el archivo:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class UserAvatarController extends Controller
{
    /**
    * Update the avatar for the user.
    *
    * @param  Request  $request
    * @return Response
    */
    public function update(Request $request)
    {
        $path = $request->file('avatar')->store('avatars');

        return $path;
    }
}

Hay algunas cosas importantes a tener en cuenta sobre este ejemplo. Observa que sólo especificamos un nombre de directorio, no un nombre de archivo. Por defecto, el método store generará un ID único que servirá como nombre de archivo. La extensión del archivo será determinada examinando el tipo MIME del archivo. La ruta al archivo será retornada por el método store para que puedas guardar la ruta, incluyendo el nombre generado, en tu base de datos.

También puedes llamar al método putFile en el facade Storage para realizar la misma manipulación de archivo del ejemplo anterior:

$path = Storage::putFile('avatars', $request->file('avatar'));

Especificando un nombre de archivo

Si no te gustaría que un nombre de archivo sea automáticamente asignado a tu archivo almacenado, puedes usar el método storeAs, que recibe una ruta, el nombre del archivo y el disco (opcional) como sus argumentos:

$path = $request->file('avatar')->storeAs(
    'avatars', $request->user()->id
);

Puedes usar el método putFileAs en el facade Storage, que realizará las mismas manipulaciones de archivos del ejemplo anterior:

$path = Storage::putFileAs(
    'avatars', $request->file('avatar'), $request->user()->id
);

Caracteres unicode no válidos y que no pueden ser mostrados serán automáticamente eliminados de rutas de archivos. Por lo tanto, podrías querer sanitizar las rutas de tus archivos antes de pasarlas a los métodos de almacenamiento de archivos de Laravel. Las rutas de archivos son normalizadas usando el método League\Flysystem\Util::normalizePath.

Especificando un disco

Por defecto, este método usará tu disco predeterminado. Si te gustaría especificar otro disco, pasa el nombre del disco como segundo argumento al método store:

$path = $request->file('avatar')->store(
    'avatars/'.$request->user()->id, 's3'
);

Otra información de archivos

Si te gustaría obtener el nombre original del archivo cargado, puedes hacer esto usando el método getClientOriginalName:

$name = $request->file('avatar')->getClientOriginalName();

El método extension puede ser usado para obtener la extensión del archivo cargado:

$extension = $request->file('avatar')->extension();

Visibilidad de archivos

En la integración de Flysystem de Laravel, «visibilidad» es una abstracción de permisos de archivos a través de múltiples plataformas. Los archivos pueden ser declarados public o private. Cuando un archivo es declarado public, estás indicando que el archivo debería ser generalmente accesible por otros. Por ejemplo, al usar el driver de S3, puedes retornar URLs para archivos public.

Puedes establecer la visibilidad al configurar el archivo mediante el método put:

use Illuminate\Support\Facades\Storage;

Storage::put('file.jpg', $contents, 'public');

Si el archivo ya ha sido almacenado, su visibilidad puede ser retornada y establecida mediante los métodos getVisibility y setVisibility:

$visibility = Storage::getVisibility('file.jpg');

Storage::setVisibility('file.jpg', 'public');

Eliminando archivos

El método delete acepta un solo nombre de archivo o un arreglo de archivos a eliminar del disco:

use Illuminate\Support\Facades\Storage;

Storage::delete('file.jpg');

Storage::delete(['file.jpg', 'file2.jpg']);

Si es necesario, puedes especificar el disco en el que se debe eliminar el archivo:

use Illuminate\Support\Facades\Storage;

Storage::disk('s3')->delete('folder_path/file_name.jpg');

Directorios

Obtener todos los archivos dentro de un directorio

El método files retorna un arreglo de todos los archivos en un directorio dado. Si te gustaría retornar una lista de todos los archivos dentro de un directorio dado incluyendo subdirectorios, puedes usar el método allFiles:

use Illuminate\Support\Facades\Storage;

$files = Storage::files($directory);

$files = Storage::allFiles($directory);

Obtener todos los directorios dentro de un directorio

El método directories retorna un arreglo de todos los directorios dentro de un directorio dado. Adicionalmente, puedes usar el método allDirectories para obtener una lista de todos los directorios dentro de un directorio dado y todos sus subdirectorios:

$directories = Storage::directories($directory);

// Recursive...
$directories = Storage::allDirectories($directory);

Crear un directorio

El método makeDirectory creará el directorio dado, incluyendo cualquier subdirectorio necesario:

Storage::makeDirectory($directory);

Eliminar un directorio

Finalmente, el método deleteDirectory puede ser usado para eliminar un directorio y todos sus archivos:

Storage::deleteDirectory($directory);

Sistemas de archivos personalizados

La integración de Flysystem de Laravel proporciona drivers para múltiples «drivers» de forma predeterminada; sin embargo, Flysystem no está limitado a estos y tiene adaptadores para muchos otros sistemas de almacenamiento. Puedes crear un driver personalizado si quieres usar alguno de los adaptadores adicionales en tu aplicación de Laravel.

Para configurar el sistema de archivos personalizado necesitarás un adaptador de Flysystem. Vamos a agregar un adaptador de Dropbox mantenido por la comunidad a nuestro proyecto:

composer require spatie/flysystem-dropbox

Luego, debes crear un proveedor de servicios como DropboxServiceProvider. En el método boot del proveedor, puedes usar el método extend del facade Storage para definir el driver personalizado:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
use Storage;

class DropboxServiceProvider extends ServiceProvider
{
    /**
    * Register any application services.
    *
    * @return void
    */
    public function register()
    {
        //
    }

    /**
    * Bootstrap any application services.
    *
    * @return void
    */
    public function boot()
    {
        Storage::extend('dropbox', function ($app, $config) {
            $client = new DropboxClient(
                $config['authorization_token']
            );

            return new Filesystem(new DropboxAdapter($client));
        });
    }
}

El primer argumento del método extend es el nombre del driver y el segundo es una Closure que recibe las variables $app y $config. La Closure de resolver debe retornar una instancia de League\Flysystem\Filesystem. La variable $config contiene los valores definidos en config/filesystems.php para el disco especificado.

Luego, registra el proveedor de servicios en tu archivo de configuración config/app.php:

'providers' => [
    // ...
    App\Providers\DropboxServiceProvider::class,
];

Una vez que hayas creado y registrado el proveedor de servicios de la extensión, puedes usar el driver dropbox en tu archivo de configuración config/filesystems.php.

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Eventos - Documentación de Laravel 6 Lección siguiente Helpers - Documentación de Laravel 6