Building a Laravel File Upload System with AWS S3 and Database Tracking for User-Owned Images
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.