Passer au contenu principal

Qu’est-ce que le stockage de fichiers

Laravel fournit une couche d’abstraction puissante basée sur le package PHP Flysystem. Vous manipulez disque local, SFTP, Amazon S3, etc. avec la même API — sans changer de code en basculant de backend.
Changer de driver ne change pas votre code. Vous pouvez utiliser le disque local en dev et S3 en production sans effort.

Configuration

La configuration se trouve dans config/filesystems.php où vous définissez des « disques » (combinaison driver + emplacement). Principaux drivers :
DriverUsage
localSystème de fichiers local du serveur
publicDisque local pour fichiers publics
s3Amazon S3 (ou compatible)
sftpServeur SFTP
ftpServeur FTP

Driver local

Les opérations se font relativement au root configuré. Par défaut : storage/app/private.
use Illuminate\Support\Facades\Storage;

Storage::disk('local')->put('example.txt', 'Contents');
// Écrit dans storage/app/private/example.txt

Changer le disque par défaut

Variable FILESYSTEM_DISK :
FILESYSTEM_DISK=s3

Disque public et lien symbolique

Le disque public héberge les fichiers accessibles depuis le web. Par défaut, il stocke dans storage/app/public. Pour rendre accessible depuis le serveur web, créez un lien symbolique de public/storage vers storage/app/public.
1

Créer le lien symbolique

php artisan storage:link
2

Générer l'URL du fichier

Après création du lien, utilisez asset.
echo asset('storage/file.txt');
Pour ajouter d’autres liens, configurez links dans config/filesystems.php.
'links' => [
    public_path('storage') => storage_path('app/public'),
    public_path('images') => storage_path('app/images'),
],
Pour supprimer les liens : storage:unlink.
php artisan storage:unlink

Opérations avec la façade Storage

Lecture

use Illuminate\Support\Facades\Storage;

// Contenu en chaîne
$contents = Storage::get('file.jpg');

// JSON décodé
$data = Storage::json('orders.json');

// Existence
if (Storage::exists('file.jpg')) {
    // ...
}

// Non existence
if (Storage::missing('file.jpg')) {
    // ...
}

Écriture

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

// Écriture d'un flux
Storage::put('file.jpg', $resource);

// Ajouter au début / à la fin
Storage::prepend('file.log', 'Prepended Text');
Storage::append('file.log', 'Appended Text');

// Copie / déplacement
Storage::copy('old/file.jpg', 'new/file.jpg');
Storage::move('old/file.jpg', 'new/file.jpg');
En cas d’échec, put retourne false par défaut. Configurez 'throw' => true sur le disque pour lever une exception.

Suppression

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

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

Storage::disk('s3')->delete('path/file.jpg');

Réponse de téléchargement

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

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

Génération d’URL

URL normale

use Illuminate\Support\Facades\Storage;

$url = Storage::url('file.jpg');
local retourne une URL relative (/storage/file.jpg), s3 retourne l’URL distante complète.

URL temporaire

temporaryUrl génère une URL à durée limitée. Disponible sur local et s3.
use Illuminate\Support\Facades\Storage;

$url = Storage::temporaryUrl(
    'file.jpg',
    now()->plus(minutes: 5)
);
Pour S3, des paramètres supplémentaires sont possibles.
$url = Storage::temporaryUrl(
    'file.jpg',
    now()->plus(minutes: 5),
    [
        'ResponseContentType' => 'application/octet-stream',
        'ResponseContentDisposition' => 'attachment; filename=file.jpg',
    ]
);
Pour des URLs d’upload temporaires : temporaryUploadUrl. Utile en architecture serverless (upload direct client → S3).
['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl(
    'file.jpg', now()->plus(minutes: 5)
);

Upload de fichiers

store (nom généré)

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserAvatarController extends Controller
{
    public function update(Request $request): string
    {
        // Nom généré depuis le type MIME
        $path = $request->file('avatar')->store('avatars');

        return $path;
    }
}

storeAs (nom explicite)

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

Spécifier un disque

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

Via la façade Storage

use Illuminate\Support\Facades\Storage;

// Nom généré
$path = Storage::putFile('avatars', $request->file('avatar'));

// Nom explicite
$path = Storage::putFileAs(
    'avatars',
    $request->file('avatar'),
    $request->user()->id
);
getClientOriginalName() et getClientOriginalExtension() peuvent être manipulés par l’utilisateur. Utilisez hashName() et extension().
$file = $request->file('avatar');

$name = $file->hashName();
$extension = $file->extension();

Visibilité des fichiers

Flysystem gère la visibilité (public / privé).
use Illuminate\Support\Facades\Storage;

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

$visibility = Storage::getVisibility('file.jpg');
Storage::setVisibility('file.jpg', 'public');
Pour rendre un fichier uploadé public : storePublicly.
$path = $request->file('avatar')->storePublicly('avatars', 's3');

Utiliser plusieurs disques

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

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

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

Configuration S3

Installer le package

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

Variables d’environnement

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
DigitalOcean Spaces, Cloudflare R2, Vultr Object Storage… sont accessibles via le driver s3 en configurant endpoint.
'endpoint' => env('AWS_ENDPOINT', 'https://your-endpoint.example.com'),

Métadonnées

use Illuminate\Support\Facades\Storage;

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

// Dernier modif (timestamp)
$time = Storage::lastModified('file.jpg');

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

// Chemin absolu (driver local)
$path = Storage::path('file.jpg');

Opérations sur les répertoires

use Illuminate\Support\Facades\Storage;

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

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

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

Storage::makeDirectory($directory);

Storage::deleteDirectory($directory);

Tests

Storage::fake() permet de tester sans toucher au disque réel.
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

test('avatar upload', function () {
    Storage::fake('avatars');

    $file = UploadedFile::fake()->image('avatar.jpg');

    $this->post('/user/avatar', ['avatar' => $file]);

    Storage::disk('avatars')->assertExists('avatar.jpg');

    Storage::disk('avatars')->assertMissing('other.jpg');
});
UploadedFile::fake()->image() nécessite l’extension GD.

Exemple : upload d’avatar

Validation, sauvegarde et persistance du chemin.
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Storage;

class ProfileController extends Controller
{
    public function updateAvatar(Request $request): RedirectResponse
    {
        $request->validate([
            'avatar' => ['required', 'image', 'max:2048'],
        ]);

        $user = $request->user();

        // Supprime l'ancienne image
        if ($user->avatar_path) {
            Storage::disk('public')->delete($user->avatar_path);
        }

        // Sauvegarde la nouvelle
        $path = $request->file('avatar')->store('avatars', 'public');

        $user->update(['avatar_path' => $path]);

        return back()->with('status', 'avatar-updated');
    }
}
Dans le template :
<img src="{{ Storage::disk('public')->url($user->avatar_path) }}" alt="Avatar">
Dernière modification le 13 juillet 2026