Building a Laravel File Upload System with AWS S3 and Database Tracking for User-Owned Images

Sauvik Kundu
5 min readMar 16, 2023

--

One of the most common tasks in web development is handling file uploads. In this blog, we will discuss how to handle multiple types of file uploads in Laravel and store them in AWS S3. We will also keep track of the uploaded files in the database and associate them with the user who uploaded them.

Prerequisites:

Before we start, you should have some basic knowledge of Laravel, PHP, and AWS S3. You should also have an AWS account and know how to create an S3 bucket and access keys.

Step 1: Set up the Laravel project

First, we need to create a new Laravel project. You can do this by running the following command in your terminal:

composer create-project --prefer-dist laravel/laravel laravel-file-upload

This will create a new Laravel project in a directory called laravel-file-upload.

Next, we need to set up our database. Open the .env file in your project root and set your database credentials:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_file_upload
DB_USERNAME=root
DB_PASSWORD=

Create a new database called laravel_file_upload in your MySQL server.

Now, run the following command to create the necessary database tables:

php artisan migrate

Step 2: Install AWS SDK for PHP

To interact with AWS S3, we need to install the AWS SDK for PHP. Run the following command in your terminal:

composer require aws/aws-sdk-php

Step 3: Set up AWS credentials

We need to set up our AWS credentials to access S3. Open the .env file in your project root and add the following lines:

AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_DEFAULT_REGION=us-west-2
AWS_BUCKET=your_bucket_name

Replace your_access_key, your_secret_key, us-west-2, and your_bucket_name with your actual AWS access key, secret access key, AWS region, and S3 bucket name.

Step 4: Create the file upload form

Let’s create a form that allows users to upload files. Create a new file called upload.blade.php in the resources/views directory and add the following code:

@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">File Upload</div>

<div class="card-body">
<form method="POST" action="{{ route('upload') }}" enctype="multipart/form-data">
@csrf

<div class="form-group">
<label for="file">Choose file</label>
<input type="file" name="file" id="file" class="form-control">
</div>

<button type="submit" class="btn btn-primary">Upload</button>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

This form has a file input field and a submit button. When the user clicks the submit button, the form will be submitted to the upload route.

Step 5: Handle the file upload

Let’s create a controller method that handles the file upload. Create a new file called UploadController.php in the app/Http/Controllers directory and add the following code:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use App\Models\File;

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

public function upload(Request $request)
{
$user = Auth::user();

if ($request->hasFile('file')) {
$file = $request->file('file');

$path = Storage::disk('s3')->putFile('/', $file, 'public');

$file = new File();
$file->user_id = $user->id;
$file->name = $file->getClientOriginalName();
$file->path = $path;
$file->save();

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

return redirect()->back()->with('error', 'File not found.');
}
}

In this code, we first check if the user is authenticated. If not, we redirect them to the login page.

Then, we check if the request contains a file. If it does, we store the file in S3 using the putFile method. We also create a new File model and save the file's details to the database. We associate the file with the current user by setting the user_id field to the user's ID.

Finally, we redirect the user back to the upload form with a success or error message.

Step 6: Create the File model

Let’s create the File model that represents a file uploaded by a user. Create a new file called File.php in the app/Models directory and add the following code:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class File extends Model
{
use HasFactory;

protected $fillable = ['user_id', 'name', 'path'];

public function user()
{
return $this->belongsTo(User::class);
}
}

n this code, we define the fields of the File model and set the fillable array to allow mass assignment of these fields. We also define a relationship between the File model and the User model, which allows us to easily retrieve the user who uploaded the file.

Step 7: Update the User model

Let’s update the User model to define a relationship with the File model. Open the User.php file in the app/Models directory and add the following code:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, Notifiable;

public function files()
{
return $this->hasMany(File::class);
}
}

In this code, we define a files relationship that allows us to retrieve all files uploaded by the user.

Step 8: Create the routes

Let’s create the routes that handle the file upload. Open the routes/web.php file and add the following code:

Route::get('/upload', [UploadController::class, 'showUploadForm'])->name('showUploadForm')->middleware('auth');
Route::post('/upload', [UploadController::class, 'upload'])->name('upload')->middleware('auth');

In this code, we define two routes: one for displaying the upload form (/upload) and one for handling the file upload (/upload). Both routes are protected with the auth middleware, which ensures that only authenticated users can access them.

Step 9: Create the view

Let’s create the view that displays the upload form. Create a new file called upload.blade.php in the resources/views directory and add the following code:

@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Upload File') }}</div>

<div class="card-body">
@if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif

@if(session('error'))
<div class="alert alert-danger">{{ session('error') }}</div>
@endif

<form method="POST" action="{{ route('upload') }}" enctype="multipart/form-data">
@csrf

<div class="form-group">
<label for="file">Choose File</label>
<input id="file" type="file" class="form-control" name="file" required>
</div>

<div class="form-group">
<button type="submit" class="btn btn-primary">{{ __('Upload') }}</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

In this code, we extend the layouts.app layout and define a form that allows the user to select a file for upload. We also display any success or error messages that were set in the controller.

Step 10: Test the application

Now that we have everything set up, let’s test the application. Start by running the application using the following command:

php artisan serve

Then, open your web browser and navigate to http://localhost:8000/upload. You should see a form that allows you to select a file for upload. Choose a file and click the "Upload" button.

If everything is set up correctly, the file should be uploaded to S3 and its details should be saved to the database. You should also see a success message on the upload form.

To verify that the file was uploaded, log in to your AWS account and navigate to the S3 console. You should see a new object in your bucket with the same name as the uploaded file.

Congratulations! You have successfully implemented a file upload system that stores files in S3 and keeps track of them in a database.

--

--

Sauvik Kundu
Sauvik Kundu

Written by Sauvik Kundu

Experienced in developing scalable, reliable, and fast-performing web applications using PHP, with a focus on Laravel. https://www.linkedin.com/in/sauvik-kundu

No responses yet