Working with Laravel File Storage and Uploads

Manage file uploads and storage using Laravel filesystem.


Handling file uploads is a core requirement for many modern web applications. Whether you’re managing user profile photos, document archives, or extensive media libraries, a robust system for file storage and retrieval is crucial. Laravel, a leading PHP framework, offers a powerful and convenient abstraction for file handling through its Filesystem. In this post, we'll explore how to work with Laravel file storage and manage file uploads efficiently.

1. Introduction to Laravel File Storage

Laravel utilizes the Flysystem PHP package by default, providing a uniform API for interacting with local, FTP, SFTP, Amazon S3, and other remote filesystems. This abstraction allows you to effortlessly swap between storage solutions without altering your application code.

The core configuration for Laravel’s filesystem lives in the config/filesystems.php file. It defines "disks," each representing a specific storage driver and location.

Common storage disks:

'disks' => [
    'local' => [
        'driver' => 'local',
        'root' => storage_path('app'),
    ],

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

    's3' => [
        'driver' => 's3',
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION'),
        'bucket' => env('AWS_BUCKET'),
        'url' => env('AWS_URL'),
    ],
],

2. Preparing for File Uploads

To allow file uploads:

  • Ensure your form uses the POST method and has enctype="multipart/form-data".
  • Configure the desired disk in filesystems.php.
  • For public access, link your storage with the filesystem using artisan:
php artisan storage:link

This command creates a public/storage symbolic link pointing to storage/app/public.


3. Handling File Uploads in Controllers

Let's build a simple file upload example. Suppose users can upload profile avatars.

Create the Upload Form

<!-- resources/views/profile/upload.blade.php -->
<form action="{{ route('profile.avatar.upload') }}" method="POST" enctype="multipart/form-data">
    @csrf
    <input type="file" name="avatar" required>
    <button type="submit">Upload Avatar</button>
</form>

The Controller Logic

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

public function uploadAvatar(Request $request)
{
    $request->validate([
        'avatar' => 'required|image|max:2048', // 2MB Max
    ]);

    if ($request->file('avatar')->isValid()) {
        $path = $request->file('avatar')->store('avatars', 'public');

        // Save $path to user's profile, etc.
        auth()->user()->update(['avatar_path' => $path]);

        return back()->with('success', 'Avatar uploaded successfully.');
    }

    return back()->withErrors('Invalid file upload.');
}

What's happening?

  • Validate the incoming request for presence, file type, and size.
  • Use the store() method, specifying the subdirectory (avatars) and the disk (public).
  • The store() method returns the relative file path.
  • Save the path to your database for future reference.

4. Retrieving and Displaying Files

To display a stored avatar:

@if(auth()->user()->avatar_path)
    <img src="{{ asset('storage/' . auth()->user()->avatar_path) }}" alt="Avatar">
@endif

For private disks (like the default local), consider generating temporary URLs or streaming files via your application for access control.


5. File Management Operations

The Storage facade allows you to perform various operations:

use Illuminate\Support\Facades\Storage;

// Check if a file exists
Storage::disk('public')->exists('avatars/example.jpg');

// Retrieve file contents
$content = Storage::disk('public')->get('avatars/example.jpg');

// Download a file (controller response)
return Storage::disk('public')->download('avatars/example.jpg');

// Delete a file
Storage::disk('public')->delete('avatars/example.jpg');

// List files in a directory
$files = Storage::disk('public')->files('avatars');

6. Using Remote Storage (Amazon S3 Example)

To use remote disks like S3:

  • Install the AWS SDK:

    composer require league/flysystem-aws-s3-v3 "^3.0"
    
  • Set credentials in your .env:

    AWS_ACCESS_KEY_ID=your-key
    AWS_SECRET_ACCESS_KEY=your-secret
    AWS_DEFAULT_REGION=us-east-1
    AWS_BUCKET=your-bucket
    
  • Update storage calls by referencing the s3 disk:

    $path = $request->file('avatar')->store('avatars', 's3');
    $url = Storage::disk('s3')->url($path);
    

7. Security Considerations

  • Validate uploads thoroughly (using MIME types, file extensions, and size limits).
  • Store sensitive or private files in protected disks.
  • Only expose public files via the public disk and storage:link.
  • Sanitize file names if storing user-supplied names.
  • Regularly audit storage for unused or orphaned files.

Conclusion

Laravel's file storage system, paired with its powerful validation and routing, makes file handling a breeze for PHP applications. Whether storing files locally or in the cloud, the Filesystem abstraction lets you write concise, maintainable, and secure storage logic. With these tools, you can confidently manage file uploads and organization in your Laravel projects.


Further Reading:

Happy coding!