Laravel File Storage¶
Laravel's filesystem abstraction (Flysystem) provides a unified API for local disk, S3, and other storage backends. File uploads from forms are handled via $request->file(), stored with the Storage facade, and served via symbolic links or signed URLs. Used in laravel validation for file rules and laravel eloquent orm models to store file paths.
Key Facts¶
- Configuration in
config/filesystems.php; default disk set viaFILESYSTEM_DISKin.env publicdisk stores files instorage/app/public/; accessible viapublic/storagesymlinkphp artisan storage:linkcreates the symlinkpublic/storage -> storage/app/public$request->file('image')returnsUploadedFileinstance$request->file('image')->store('uploads')saves tostorage/app/uploads/with random name$request->file('image')->storeAs('uploads', 'custom-name.jpg')saves with specific nameStorage::disk('public')->url($path)generates public URL for stored fileasset('storage/' . $path)generates URL when using public disk with symlink- Max upload size controlled by
upload_max_filesizeandpost_max_sizein php.ini - Validate files with
image|mimes:jpg,png|max:2048(max in KB)
Patterns¶
File upload in controller¶
<?php
use Illuminate\Support\Facades\Storage;
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'image' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
]);
// Store uploaded file
if ($request->hasFile('image')) {
$path = $request->file('image')->store('posts', 'public');
// $path = "posts/randomname.jpg"
$validated['image'] = $path;
}
Post::create($validated);
return redirect()->route('posts.index');
}
Updating file (delete old, store new)¶
<?php
public function update(Request $request, Post $post)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'image' => 'nullable|image|mimes:jpg,png,webp|max:2048',
]);
if ($request->hasFile('image')) {
// Delete old file if exists
if ($post->image) {
Storage::disk('public')->delete($post->image);
}
$validated['image'] = $request->file('image')->store('posts', 'public');
}
$post->update($validated);
return redirect()->route('posts.index');
}
Deleting files¶
<?php
public function destroy(Post $post)
{
// Delete associated file
if ($post->image) {
Storage::disk('public')->delete($post->image);
}
$post->delete();
return redirect()->route('posts.index');
}
Displaying images in Blade¶
{{-- Display uploaded image --}}
@if ($post->image)
<img src="{{ asset('storage/' . $post->image) }}" alt="{{ $post->title }}">
@else
<img src="{{ asset('images/default-post.jpg') }}" alt="Default">
@endif
Upload form¶
{{-- enctype is required for file uploads --}}
<form method="POST" action="{{ route('posts.store') }}" enctype="multipart/form-data">
@csrf
<input type="text" name="title" value="{{ old('title') }}">
<input type="file" name="image" accept="image/*">
@error('image')
<span class="text-danger">{{ $message }}</span>
@enderror
<button type="submit">Upload</button>
</form>
Storage facade operations¶
<?php
use Illuminate\Support\Facades\Storage;
// Check if file exists
Storage::disk('public')->exists('posts/image.jpg');
// Get file contents
$contents = Storage::disk('public')->get('posts/image.jpg');
// Get file URL
$url = Storage::disk('public')->url('posts/image.jpg');
// Delete file
Storage::disk('public')->delete('posts/image.jpg');
// Delete directory
Storage::disk('public')->deleteDirectory('posts');
// List files in directory
$files = Storage::disk('public')->files('posts');
// Copy / Move
Storage::disk('public')->copy('old/file.jpg', 'new/file.jpg');
Storage::disk('public')->move('old/file.jpg', 'new/file.jpg');
Gotchas¶
| Symptom | Cause | Fix |
|---|---|---|
| Images show broken link | storage:link not run | Run php artisan storage:link |
enctype missing -> file not received | Form missing multipart/form-data | Add enctype="multipart/form-data" to <form> |
| Upload fails silently | File exceeds php.ini limits | Increase upload_max_filesize and post_max_size |
| Old file not deleted on update | Delete logic not implemented | Call Storage::delete() before storing new file |
| File URL 404 after deploy | Symlink broken or not created on server | Run storage:link on server; some hosts don't support symlinks |
| Orphaned files accumulate | Files not deleted when model deleted | Delete files in model's deleting event or controller |
See Also¶
- laravel validation - file validation rules (image, mimes, max)
- laravel blade templates - displaying uploaded images
- laravel eloquent orm - storing file paths in model columns
- https://laravel.com/docs/11.x/filesystem
- https://laravel.com/docs/11.x/requests#files