Saltar contenido

Almacenamiento de Archivos

Introducción

Laravel proporciona una potente abstracción de sistema de archivos gracias al maravilloso paquete PHP Flysystem de Frank de Jonge. La integración de Laravel con Flysystem ofrece controladores simples para trabajar con sistemas de archivos locales, SFTP y Amazon S3. Aún mejor, es asombrosamente sencillo cambiar entre estas opciones de almacenamiento entre tu máquina de desarrollo local y el servidor de producción, ya que la API sigue siendo la misma para cada sistema.

Configuración

El archivo de configuración del sistema de archivos de Laravel se encuentra en config/filesystems.php. Dentro de este archivo, puedes configurar todos tus "discos" de sistema de archivos. Cada disco representa un controlador de almacenamiento particular y una ubicación de almacenamiento. Se incluyen configuraciones de ejemplo para cada controlador soportado en el archivo de configuración para que puedas modificar la configuración según tus preferencias de almacenamiento y credenciales. El driver local interactúa con archivos almacenados localmente en el servidor que ejecuta la aplicación Laravel, mientras que el driver s3 se utiliza para escribir en el servicio de almacenamiento en la nube S3 de Amazon.

[!NOTA] Puedes configurar tantos discos como desees y incluso puedes tener múltiples discos que utilicen el mismo driver.

El Driver Local

Al usar el driver local, todas las operaciones de archivo son relativas al directorio root definido en tu archivo de configuración filesystems. Por defecto, este valor se establece en el directorio storage/app. Por lo tanto, el siguiente método escribiría en storage/app/example.txt:

use Illuminate\Support\Facades\Storage;
 
Storage::disk('local')->put('example.txt', 'Contents');

El Disco Público

El disco public incluido en el archivo de configuración filesystems de tu aplicación está destinado a archivos que serán de acceso público. Por defecto, el disco public utiliza el driver local y almacena sus archivos en storage/app/public. Para hacer que estos archivos sean accesibles desde la web, debes crear un enlace simbólico de public/storage a storage/app/public. Utilizar esta convención de carpetas mantendrá tus archivos accesibles públicamente en un solo directorio que se puede compartir fácilmente entre despliegues al usar sistemas de despliegue sin tiempo de inactividad como Envoyer. Para crear el enlace simbólico, puedes usar el comando Artisan storage:link:

php artisan storage:link

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

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

Puedes configurar enlaces simbólicos adicionales en tu archivo de configuración filesystems. Cada uno de los enlaces configurados se creará cuando ejecutes el comando storage:link:

'links' => [
public_path('storage') => storage_path('app/public'),
public_path('images') => storage_path('app/images'),
],

El comando storage:unlink se puede utilizar para destruir tus enlaces simbólicos configurados:

php artisan storage:unlink

Prerrequisitos del Driver

Configuración del Driver S3

Antes de usar el driver S3, necesitarás instalar el paquete Flysystem S3 a través del gestor de paquetes Composer:

composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies

Un array de configuración del disco S3 se encuentra en tu archivo de configuración config/filesystems.php. Típicamente, debes configurar tu información y credenciales de S3 utilizando las siguientes variables de entorno, que son referenciadas por el archivo de configuración config/filesystems.php:

AWS_ACCESS_KEY_ID=<your-key-id>
AWS_SECRET_ACCESS_KEY=<your-secret-access-key>
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=<your-bucket-name>
AWS_USE_PATH_STYLE_ENDPOINT=false

Para conveniencia, estas variables de entorno coinciden con la convención de nomenclatura utilizada por la AWS CLI.

Configuración del driver FTP

Antes de usar el driver FTP, necesitarás instalar el paquete Flysystem FTP a través del gestor de paquetes Composer:

composer require league/flysystem-ftp "^3.0"

Las integraciones de Flysystem de Laravel funcionan muy bien con FTP; sin embargo, no se incluye una configuración de muestra con el archivo de configuración config/filesystems.php predeterminado del framework. Si necesitas configurar un sistema de archivos FTP, puedes usar el ejemplo de configuración a continuación:

'ftp' => [
'driver' => 'ftp',
'host' => env('FTP_HOST'),
'username' => env('FTP_USERNAME'),
'password' => env('FTP_PASSWORD'),
 
// Optional FTP Settings...
// 'port' => env('FTP_PORT', 21),
// 'root' => env('FTP_ROOT'),
// 'passive' => true,
// 'ssl' => true,
// 'timeout' => 30,
],

Configuración del Driver SFTP

Antes de usar el driver SFTP, necesitarás instalar el paquete Flysystem SFTP a través del gestor de paquetes Composer:

composer require league/flysystem-sftp-v3 "^3.0"

Las integraciones de Flysystem de Laravel funcionan muy bien con SFTP; sin embargo, no se incluye una configuración de muestra con el archivo de configuración predeterminado config/filesystems.php del framework. Si necesitas configurar un sistema de archivos SFTP, puedes usar el ejemplo de configuración a continuación:

'sftp' => [
'driver' => 'sftp',
'host' => env('SFTP_HOST'),
 
// Settings for basic authentication...
'username' => env('SFTP_USERNAME'),
'password' => env('SFTP_PASSWORD'),
 
// Settings for SSH key based authentication with encryption password...
'privateKey' => env('SFTP_PRIVATE_KEY'),
'passphrase' => env('SFTP_PASSPHRASE'),
 
// Settings for file / directory permissions...
'visibility' => 'private', // `private` = 0600, `public` = 0644
'directory_visibility' => 'private', // `private` = 0700, `public` = 0755
 
// Optional SFTP Settings...
// 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'),
// 'maxTries' => 4,
// 'passphrase' => env('SFTP_PASSPHRASE'),
// 'port' => env('SFTP_PORT', 22),
// 'root' => env('SFTP_ROOT', ''),
// 'timeout' => 30,
// 'useAgent' => true,
],

Sistemas de Archivos con Alcance y de Solo Lectura

Los discos con alcance te permiten definir un sistema de archivos donde todas las rutas se prefijan automáticamente con un prefijo de ruta dado. Antes de crear un disco de sistema de archivos con alcance, necesitarás instalar un paquete adicional de Flysystem a través del gestor de paquetes Composer:

composer require league/flysystem-path-prefixing "^3.0"

Puedes crear una instancia de disco con alcance de ruta de cualquier disco del sistema de archivos existente definiendo un disco que utilice el driver scoped. Por ejemplo, puedes crear un disco que limite tu disco s3 existente a un prefijo de ruta específico, y luego cada operación de archivo utilizando tu disco con alcance utilizará el prefijo especificado:

's3-videos' => [
'driver' => 'scoped',
'disk' => 's3',
'prefix' => 'path/to/videos',
],

Los discos "solo lectura" te permiten crear discos de sistema de archivos que no permiten operaciones de escritura. Antes de usar la opción de configuración read-only, necesitarás instalar un paquete adicional de Flysystem a través del gestor de paquetes Composer:

composer require league/flysystem-read-only "^3.0"

A continuación, puedes incluir la opción de configuración read-only en uno o más de los arrays de configuración de tu disco:

's3-videos' => [
'driver' => 's3',
// ...
'read-only' => true,
],

Sistemas de Archivos Compatibles con Amazon S3

Por defecto, el archivo de configuración filesystems de tu aplicación contiene una configuración de disco para el disco s3. Además de usar este disco para interactuar con Amazon S3, puedes usarlo para interactuar con cualquier servicio de almacenamiento de archivos compatible con S3, como MinIO o DigitalOcean Spaces. Típicamente, después de actualizar las credenciales del disco para que coincidan con las credenciales del servicio que planeas utilizar, solo necesitas actualizar el valor de la opción de configuración endpoint. El valor de esta opción se define típicamente a través de la variable de entorno AWS_ENDPOINT:

'endpoint' => env('AWS_ENDPOINT', 'https://minio:9000'),

MinIO

Para que la integración de Flysystem de Laravel genere URLs adecuadas al usar MinIO, debes definir la variable de entorno AWS_URL para que coincida con la URL local de tu aplicación e incluya el nombre del bucket en la ruta de la URL:

AWS_URL=http://localhost:9000/local

[!WARNING] No se admite la generación de URLs de almacenamiento temporales mediante el método temporaryUrl al usar MinIO.

Obtención de Instancias de Disco

La facade Storage puede utilizarse para interactuar con cualquiera de tus discos configurados. Por ejemplo, puedes usar el método put en la facade para almacenar un avatar en el disco predeterminado. Si llamas a métodos en la facade Storage sin llamar primero al método disk, el método se pasará automáticamente al disco predeterminado:

use Illuminate\Support\Facades\Storage;
 
Storage::put('avatars/1', $content);

Si tu aplicación interactúa con múltiples discos, puedes usar el método disk en la fachada Storage para trabajar con archivos en un disco particular:

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

Discos Bajo Demanda

A veces es posible que desees crear un disco en tiempo de ejecución utilizando una configuración dada sin que esa configuración esté presente en el archivo de configuración filesystems de tu aplicación. Para lograr esto, puedes pasar un array de configuración al método build de la fachada Storage:

use Illuminate\Support\Facades\Storage;
 
$disk = Storage::build([
'driver' => 'local',
'root' => '/path/to/root',
]);
 
$disk->put('image.jpg', $content);

Recuperando Archivos

El método get puede utilizarse para recuperar el contenido de un archivo. La cadena de contenido en bruto del archivo será devuelta por el método. Recuerda, todas las rutas de archivo deben especificarse en relación con la ubicación "root" del disco:

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

Si el archivo que estás recuperando contiene JSON, puedes usar el método json para recuperar el archivo y decodificar su contenido:

$orders = Storage::json('orders.json');

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

if (Storage::disk('s3')->exists('file.jpg')) {
// ...
}

El método missing se puede utilizar para determinar si un archivo falta del disco:

if (Storage::disk('s3')->missing('file.jpg')) {
// ...
}

Descargando Archivos

El método download se puede utilizar para generar una respuesta que fuerce al navegador del usuario a descargar el archivo en la ruta dada. El método download acepta un nombre de archivo como segundo argumento del método, que determinará el nombre del archivo que verá el usuario al descargar el archivo. Finalmente, puedes pasar un array de encabezados HTTP como tercer argumento al método:

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

URL de archivos

Puedes usar el método url para obtener la URL de un archivo dado. Si estás utilizando el driver local, esto típicamente solo añadirá /storage al camino dado y devolverá una URL relativa al archivo. Si estás utilizando el driver s3, se devolverá la URL remota completamente calificada:

use Illuminate\Support\Facades\Storage;
 
$url = Storage::url('file.jpg');

Al utilizar el driver local, todos los archivos que deben ser accesibles públicamente deben colocarse en el directorio storage/app/public. Además, debes crear un enlace simbólico en public/storage que apunte al directorio storage/app/public.

[!WARNING] Al utilizar el driver local, el valor de retorno de url no está codificado en URL. Por esta razón, recomendamos almacenar siempre sus archivos utilizando nombres que creen URL válidas.

Personalización del Host URL

Si deseas modificar el host para las URL generadas utilizando la fachada Storage, puedes agregar o cambiar la opción url en el array de configuración del disco:

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

URLs Temporales

Usando el método temporaryUrl, puedes crear URL temporales para archivos almacenados utilizando el driver s3. Este método acepta una ruta y una instancia de DateTime especificando cuándo debe expirar la URL:

use Illuminate\Support\Facades\Storage;
 
$url = Storage::temporaryUrl(
'file.jpg', now()->addMinutes(5)
);

Si necesitas especificar parámetros de solicitud adicionales de S3, puedes pasar el array de parámetros de solicitud como el tercer argumento al método temporaryUrl:

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

Si necesitas personalizar cómo se crean las URL temporales para un disco de almacenamiento específico, puedes usar el método buildTemporaryUrlsUsing. Por ejemplo, esto puede ser útil si tienes un controlador que te permite descargar archivos almacenados a través de un disco que no admite típicamente URL temporales. Por lo general, este método debe ser llamado desde el método boot de un proveedor de servicios:

<?php
 
namespace App\Providers;
 
use DateTime;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Storage::disk('local')->buildTemporaryUrlsUsing(
function (string $path, DateTime $expiration, array $options) {
return URL::temporarySignedRoute(
'files.download',
$expiration,
array_merge($options, ['path' => $path])
);
}
);
}
}

URL de Carga Temporal

[!WARNING] La capacidad de generar URL de carga temporales solo es compatible con el driver s3. Si necesitas generar una URL temporal que se pueda usar para subir un archivo directamente desde tu aplicación del lado del cliente, puedes usar el método temporaryUploadUrl. Este método acepta una ruta y una instancia de DateTime especificando cuándo debe expirar la URL. El método temporaryUploadUrl devuelve un array asociativo que se puede desestructurar en la URL de carga y los encabezados que se deben incluir con la solicitud de carga:

use Illuminate\Support\Facades\Storage;
 
['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl(
'file.jpg', now()->addMinutes(5)
);

Este método es principalmente útil en entornos serverless que requieren que la aplicación del lado del cliente suba archivos directamente a un sistema de almacenamiento en la nube como Amazon S3.

Metadatos del Archivo

Además de leer y escribir archivos, Laravel también puede proporcionar información sobre los propios archivos. Por ejemplo, se puede utilizar el método size para obtener el tamaño de un archivo en bytes:

use Illuminate\Support\Facades\Storage;
 
$size = Storage::size('file.jpg');

El método lastModified devuelve el timestamp UNIX de la última vez que se modificó el archivo:

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

El tipo MIME de un archivo dado se puede obtener a través del método mimeType:

$mime = Storage::mimeType('file.jpg');

Rutas de Archivos

Puedes usar el método path para obtener la ruta de un archivo dado. Si estás usando el driver local, esto devolverá la ruta absoluta al archivo. Si estás usando el driver s3, este método devolverá la ruta relativa al archivo en el bucket de S3:

use Illuminate\Support\Facades\Storage;
 
$path = Storage::path('file.jpg');

Almacenando Archivos

El método put se puede utilizar para almacenar los contenidos de un archivo en un disco. También puedes pasar un recurso PHP al método put, que utilizará el soporte de flujo subyacente de Flysystem. Recuerda que todas las rutas de archivo deben especificarse en relación con la ubicación "raíz" configurada para el disco:

use Illuminate\Support\Facades\Storage;
 
Storage::put('file.jpg', $contents);
 
Storage::put('file.jpg', $resource);

Escrituras Fallidas

Si el método put (u otras operaciones de "escritura") no puede escribir el archivo en disco, se devolverá false:

if (! Storage::put('file.jpg', $contents)) {
// The file could not be written to disk...
}

Si lo deseas, puedes definir la opción throw dentro del array de configuración del disco de tu sistema de archivos. Cuando esta opción se define como true, métodos de "escritura" como put lanzarán una instancia de League\Flysystem\UnableToWriteFile cuando las operaciones de escritura fallen:

'public' => [
'driver' => 'local',
// ...
'throw' => true,
],

Añadiendo y Quitando de Archivos

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

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

Copiando y Moviendo Archivos

El método copy se puede usar para copiar un archivo existente a una nueva ubicación en el disco, mientras que el método move se puede usar 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');

Transmisión Automática

Transmitir archivos a almacenamiento ofrece un uso de memoria significativamente reducido. Si deseas que Laravel gestione automáticamente la transmisión de un archivo dado a tu ubicación de almacenamiento, puedes usar el método putFile o putFileAs. Este método acepta una instancia de Illuminate\Http\File o Illuminate\Http\UploadedFile y transmitirá automáticamente el archivo a tu ubicación deseada:

use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
 
// Automatically generate a unique ID for filename...
$path = Storage::putFile('photos', new File('/path/to/photo'));
 
// Manually specify a filename...
$path = Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

Hay algunas cosas importantes a tener en cuenta sobre el método putFile. Ten en cuenta que solo especificamos un nombre de directorio y no un nombre de archivo. Por defecto, el método putFile generará un ID único para servir como el nombre del archivo. La extensión del archivo se determinará examinando el tipo MIME del archivo. La ruta al archivo será devuelta por el método putFile, para que puedas 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 especialmente útil si estás almacenando el archivo en un disco en la nube como Amazon S3 y te gustaría que el archivo sea accesible públicamente a través de las URL generadas:

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

Cargas de Archivos

En aplicaciones web, uno de los casos de uso más comunes para almacenar archivos es el almacenamiento de archivos subidos por usuarios, como fotos y documentos. Laravel facilita mucho el almacenamiento de archivos subidos utilizando el método store en una instancia de archivo subido. Llama al método store con la ruta en la que deseas almacenar el archivo subido:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
 
class UserAvatarController extends Controller
{
/**
* Update the avatar for the user.
*/
public function update(Request $request): string
{
$path = $request->file('avatar')->store('avatars');
 
return $path;
}
}

Hay algunas cosas importantes a tener en cuenta sobre este ejemplo. Ten en cuenta que solo especificamos un nombre de directorio, no un nombre de archivo. Por defecto, el método store generará un ID único para servir como el nombre del archivo. La extensión del archivo se determinará examinando el tipo MIME del archivo. La ruta al archivo será devuelta por el método store, para que puedas almacenar la ruta, incluyendo el nombre de archivo generado, en tu base de datos. También puedes llamar al método putFile en la fachada Storage para realizar la misma operación de almacenamiento de archivos que el ejemplo anterior:

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

Especificando un Nombre de Archivo

Si no deseas que se le asigne un nombre de archivo automáticamente a tu archivo almacenado, puedes usar el método storeAs, que recibe la ruta, el nombre del archivo y el disco (opcional) como sus argumentos:

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

También puedes usar el método putFileAs en la fachada Storage, que realizará la misma operación de almacenamiento de archivos que el ejemplo anterior:

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

[!WARNING] Los caracteres unicode no imprimibles e inválidos serán eliminados automáticamente de las rutas de archivo. Por lo tanto, es posible que desees sanear tus rutas de archivo antes de pasarlas a los métodos de almacenamiento de archivos de Laravel. Las rutas de archivo se normalizan utilizando el método League\Flysystem\WhitespacePathNormalizer::normalizePath.

Especificando un Disco

Por defecto, el método store de este archivo subido utilizará tu disco predeterminado. Si deseas 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'
);

Si estás utilizando el método storeAs, puedes pasar el nombre del disco como el tercer argumento al método:

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

Otra Información del Archivo Subido

Si deseas obtener el nombre y la extensión originales del archivo subido, puedes hacerlo utilizando los métodos getClientOriginalName y getClientOriginalExtension:

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

Sin embargo, ten en cuenta que los métodos getClientOriginalName y getClientOriginalExtension se consideran inseguros, ya que el nombre y la extensión del archivo pueden ser manipulados por un usuario malintencionado. Por esta razón, generalmente deberías preferir los métodos hashName y extension para obtener un nombre y una extensión para la carga de archivos dada:

$file = $request->file('avatar');
 
$name = $file->hashName(); // Generate a unique, random name...
$extension = $file->extension(); // Determine the file's extension based on the file's MIME type...

Visibilidad de Archivos

En la integración de Flysystem de Laravel, "visibilidad" es una abstracción de los permisos de archivo en múltiples plataformas. Los archivos pueden declararse public o private. Cuando un archivo se declara public, estás indicando que el archivo debería ser accesible en general para otros. Por ejemplo, al usar el driver S3, puedes recuperar URL para archivos public. Puedes establecer la visibilidad al escribir el archivo a través del 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 recuperada y configurada a través de los métodos getVisibility y setVisibility:

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

Al interactuar con los archivos subidos, puedes usar los métodos storePublicly y storePubliclyAs para almacenar el archivo subido con visibilidad pública:

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

Archivos Locales y Visibilidad

Al usar el driver local, la visibilidad public se traduce a permisos 0755 para directorios y 0644 para archivos. Puedes modificar los mapeos de permisos en el archivo de configuración filesystems de tu aplicación:

'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'permissions' => [
'file' => [
'public' => 0644,
'private' => 0600,
],
'dir' => [
'public' => 0755,
'private' => 0700,
],
],
'throw' => false,
],

Eliminando Archivos

El método delete acepta un solo nombre de archivo o un array de archivos para eliminar:

use Illuminate\Support\Facades\Storage;
 
Storage::delete('file.jpg');
 
Storage::delete(['file.jpg', 'file2.jpg']);

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

use Illuminate\Support\Facades\Storage;
 
Storage::disk('s3')->delete('path/file.jpg');

Directorios

Obtener Todos los Archivos Dentro de un Directorio

El método files devuelve un array con todos los archivos en un directorio dado. Si deseas obtener una lista de todos los archivos dentro de un directorio dado, incluyendo todos los 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 devuelve un array de todas las directorios dentro de un directorio dado. Además, 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);
 
$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 se puede utilizar para eliminar un directorio y todos sus archivos:

Storage::deleteDirectory($directory);

Pruebas

El método fake de la fachada Storage te permite generar fácilmente un disco simulado que, combinado con las utilidades de generación de archivos de la clase Illuminate\Http\UploadedFile, simplifica en gran medida las pruebas de cargas de archivos. Por ejemplo:

<?php
 
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
 
test('albums can be uploaded', function () {
Storage::fake('photos');
 
$response = $this->json('POST', '/photos', [
UploadedFile::fake()->image('photo1.jpg'),
UploadedFile::fake()->image('photo2.jpg')
]);
 
// Assert one or more files were stored...
Storage::disk('photos')->assertExists('photo1.jpg');
Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
 
// Assert one or more files were not stored...
Storage::disk('photos')->assertMissing('missing.jpg');
Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
 
// Assert that a given directory is empty...
Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
});
<?php
 
namespace Tests\Feature;
 
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
 
class ExampleTest extends TestCase
{
public function test_albums_can_be_uploaded(): void
{
Storage::fake('photos');
 
$response = $this->json('POST', '/photos', [
UploadedFile::fake()->image('photo1.jpg'),
UploadedFile::fake()->image('photo2.jpg')
]);
 
// Assert one or more files were stored...
Storage::disk('photos')->assertExists('photo1.jpg');
Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
 
// Assert one or more files were not stored...
Storage::disk('photos')->assertMissing('missing.jpg');
Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
 
// Assert that a given directory is empty...
Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
}
}

Por defecto, el método fake eliminará todos los archivos en su directorio temporal. Si deseas mantener estos archivos, puedes usar el método "persistentFake" en su lugar. Para obtener más información sobre las pruebas de cargas de archivos, puedes consultar la documentación de pruebas HTTP sobre cargas de archivos.

[!WARNING] El método image requiere la extensión GD.

Sistemas de Archivos Personalizados

La integración de Flysystem de Laravel proporciona soporte para varios "drivers" de manera predeterminada; sin embargo, Flysystem no se limita a estos y tiene adaptadores para muchos otros sistemas de almacenamiento. Puedes crear un driver personalizado si deseas usar uno de estos adaptadores adicionales en tu aplicación Laravel. Para definir un sistema de archivos personalizado, necesitarás un adaptador de Flysystem. Vamos a añadir un adaptador de Dropbox mantenido por la comunidad a nuestro proyecto:

composer require spatie/flysystem-dropbox

A continuación, puedes registrar el driver dentro del método boot de uno de los proveedores de servicios de tu aplicación. Para lograr esto, debes usar el método extend de la fachada Storage:

<?php
 
namespace App\Providers;
 
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Storage::extend('dropbox', function (Application $app, array $config) {
$adapter = new DropboxAdapter(new DropboxClient(
$config['authorization_token']
));
 
return new FilesystemAdapter(
new Filesystem($adapter, $config),
$adapter,
$config
);
});
}
}

El primer argumento del método extend es el nombre del driver y el segundo es una función anónima que recibe las variables $app y $config. La función anónima debe devolver una instancia de Illuminate\Filesystem\FilesystemAdapter. La variable $config contiene los valores definidos en config/filesystems.php para el disco especificado. 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.