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 :
| Driver | Usage |
|---|
local | Système de fichiers local du serveur |
public | Disque local pour fichiers publics |
s3 | Amazon S3 (ou compatible) |
sftp | Serveur SFTP |
ftp | Serveur 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 :
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.
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">