Laravel file storage with disk abstraction

Carlos Mendez Jan 2026
3 tabs
<?php

namespace App\Http\Controllers;

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

class UploadController extends Controller
{
    public function store(Request $request)
    {
        $request->validate([
            'file' => 'required|file|max:10240', // 10MB
            'avatar' => 'nullable|image|max:2048',
        ]);

        // Store to default disk
        $path = $request->file('file')->store('uploads');

        // Store to specific disk with custom name
        $avatarPath = $request->file('avatar')->storeAs(
            'avatars',
            $request->user()->id . '.jpg',
            'public'
        );

        // Store with automatic hashing
        $hashedPath = $request->file('file')->store('documents', 's3');

        // Get file URL
        $url = Storage::disk('public')->url($avatarPath);

        // Temporary URL for private files (S3)
        $temporaryUrl = Storage::disk('s3')->temporaryUrl(
            $hashedPath,
            now()->addMinutes(5)
        );

        return response()->json([
            'path' => $path,
            'url' => $url,
            'temporary_url' => $temporaryUrl,
        ]);
    }

    public function download(string $path)
    {
        if (!Storage::exists($path)) {
            abort(404);
        }

        return Storage::download($path);
    }

    public function delete(string $path)
    {
        Storage::delete($path);

        // Delete multiple files
        Storage::delete(['file1.jpg', 'file2.jpg']);

        // Delete directory
        Storage::deleteDirectory('old-uploads');

        return response()->json(['message' => 'File deleted']);
    }
}
3 files · php Explain with highlit

Laravel's filesystem abstraction treats local, S3, FTP, and other storage identically. I configure disks in config/filesystems.php with drivers and credentials. The Storage facade provides methods like put(), get(), delete() that work across all disks. File uploads use store() or storeAs() to save to configured disks. Public disks generate accessible URLs via url(), while private disks use temporary URLs with expiration. I switch environments by changing disk configuration—local in dev, S3 in production. Disk visibility controls public/private access. File streaming handles large files without memory issues. This abstraction makes storage backend changes trivial without code modifications.