Image Upload in Laravel: A Comprehensive Guide

Uploading files is a common requirement in web applications. Laravel provides a robust framework to handle file uploads with ease, ensuring security and validation. This guide will walk you through setting up image uploads in Laravel, first using local storage and then extending it to support Amazon S3.

Part 1: Local Storage
Step 1: Create the Upload Controller
Generate a controller to manage the file upload logic:

php artisan make:controller UploadController


Step 2: Create the Form Request Class
Generate a Form Request class for validation:

php artisan make:request UploadFileRequest


Step 3: Define Validation Logic in the Form Request
Open app/Http/Requests/UploadFileRequest.php and define the validation rules:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\File;

class UploadFileRequest extends FormRequest
{
    public function authorize()
    {
        return true; // Adjust this if you have authorization logic
    }

    public function rules()
    {
        return [
            'file' => [
                'required',
                File::image()
                    ->dimensions(Rule::dimensions()->minWidth(100)->minHeight(100)->maxWidth(2000)->maxHeight(2000))
                    ->max(2048),
            ],
        ];
    }

    public function messages()
    {
        return [
            'file.required' => 'An image file is required.',
            'file.image' => 'The file must be an image.',
            'file.dimensions' => 'The image dimensions must be between 100x100 and 2000x2000 pixels.',
            'file.max' => 'The image size must not exceed 2MB.',
        ];
    }
}


Step 4: Define Routes
Open routes/web.php and define the routes for the upload form and handling the file upload:

<?php

use App\Http\Controllers\UploadController;

Route::get('/upload', [UploadController::class, 'showUploadForm'])->name('upload.form');
Route::post('/upload', [UploadController::class, 'uploadFile'])->name('upload.file');


Step 5: Create the Upload Form
Create a Blade template for the upload form in resources/views/upload.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h1>Upload File</h1>
    @if ($errors->any())
        <div>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    @if (session('success'))
        <div>
            {{ session('success') }}
        </div>
        <img src="{{ session('path') }}" alt="Uploaded Image">
    @endif

    <form action="{{ route('upload.file') }}" method="POST" enctype="multipart/form-data">
        @csrf
        <label for="file">Choose a file:</label>
        <input type="file" name="file" id="file">
        <button type="submit">Upload</button>
    </form>
</body>
</html>

Step 6: Implement the Controller Logic
Open app/Http/Controllers/UploadController.php and implement the methods:

<?php
namespace App\Http\Controllers;

use App\Http\Requests\UploadFileRequest;
use Illuminate\Support\Facades\Storage;

class UploadController extends Controller
{
    public function showUploadForm()
    {
        return view('upload');
    }

    public function uploadFile(UploadFileRequest $request)
    {
        if ($request->file('file')->isValid()) {
            $file = $request->file('file');
            $path = $file->store('uploads', 'public');

            return back()->with('success', 'File uploaded successfully!')->with('path', Storage::url($path));
        }

        return back()->withErrors(['file' => 'File upload failed.']);
    }
}


Step 7: Configure the Filesystem
Ensure your config/filesystems.php is configured to use the public disk:

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


Run the following command to create a symbolic link from public/storage to storage/app/public:

php artisan storage:link

 

Part 2: Amazon S3 Storage
To extend the functionality to support Amazon S3, follow these additional steps.

Step 1: Set Up AWS S3

  • Create an S3 Bucket:
    Go to the S3 service in the AWS Management Console.
    Create a new bucket and configure its permissions as required.

  • Get AWS Credentials:
    Go to the IAM service in the AWS Management Console.
    Create a new user with Programmatic access.
    Attach the AmazonS3FullAccess policy (or a more restrictive policy as needed).
    Download the credentials file or note down the Access Key ID and Secret Access Key.

Step 2: Configure Laravel to Use S3
Add your AWS credentials to your .env file:

AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_DEFAULT_REGION=your-region (e.g., us-east-1)
AWS_BUCKET=your-bucket-name
AWS_URL=https://your-bucket-name.s3.amazonaws.com


Update the config/filesystems.php file to include your S3 configuration:

'disks' => [
    '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'),
        'visibility' => 'public',
    ],
],


Step 3: Implement the Controller Logic for S3 Upload
Modify the UploadController to upload files to S3:

<?php
namespace App\Http\Controllers;

use App\Http\Requests\UploadFileRequest;
use Illuminate\Support\Facades\Storage;

class UploadController extends Controller
{
    public function showUploadForm()
    {
        return view('upload');
    }

    public function uploadFile(UploadFileRequest $request)
    {
        if ($request->file('file')->isValid()) {
            $file = $request->file('file');
            $path = $file->store('uploads', 's3');

            // Make the file publicly accessible
            Storage::disk('s3')->setVisibility($path, 'public');
            $url = Storage::disk('s3')->url($path);

            return back()->with('success', 'File uploaded successfully!')->with('path', $url);
        }

        return back()->withErrors(['file' => 'File upload failed.']);
    }
}

Step 4: Update the Upload Form
Update the form to allow file uploads without any changes, as the controller already handles the storage to S3:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h1>Upload File</h1>
    @if ($errors->any())
        <div>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    @if (session('success'))
        <div>
            {{ session('success') }}
        </div>
        <img src="{{ session('path') }}" alt="Uploaded Image">
    @endif

    <form action="{{ route('upload.file') }}" method="POST" enctype="multipart/form-data">
        @csrf
        <label for="file">Choose a file:</label>
        <input type="file" name="file" id="file">
        <button type="submit">Upload</button>
    </form>
</body>
</html>


Conclusion
By following this comprehensive guide, you have set up a robust file upload system in Laravel that supports both local storage and Amazon S3. These setups ensure a scalable and flexible file upload solution for your Laravel applications. Feel free to customize the validation, error messages, and storage configurations as needed to suit your specific requirements.

Also Read:
Image Upload in PHP: A Step-by-Step Guide