Laravel File Upload: A Complete Tutorial and Guide
Last Updated on February 15, 2024
Working with files in any application is a crucial part of any system. Laravel provides a filesystem from where we can interact with files be it file uploads, file storage, or file retrievals.
If you have your application deployed to production using one of the hosting providers, It is important to know how to manage your resources wisely and minimize the size of your videos or images. One important thing we could do is compress the files/images so that they do not occupy a lot of space in your server or any storage service such as Amazon S3, Google Cloud Storage, or any other external disk services.
In this article, I am going to show you how to upload and compress files in Laravel before storing them.
How do you Upload Files in Laravel?
To upload files in Laravel we can follow the following steps:
Step 1: Create a Laravel Project
We can use the Laravel new command or composer
laravel new test
Or
composer create-project laravel/laravel test
Step 2: Add Auth Scaffolding
In this step, we can use Laravel breeze to scaffold authentication and all other necessary requirements such as password reset and email confirmation logic.
composer require laravel/breeze --dev
php artisan breeze:install
php artisan migrate
npm install
npm run dev
Step 3: Add Model and Migration
The next step is to create a migration file that will contain the file name and folder. We can use artisan to create a model and migration file.
php artisan make:model Files -m
The -m flag creates a migration file for the Files Model. We can add the following logic to the Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Files extends Model
{
use HasFactory;
protected $fillable = ['path'];
}
We can then specify the columns in the files table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('files', function (Blueprint $table) {
$table->id();
$table->string('path');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('files');
}
};
Step 4: Create a generic File Upload Trait
Traits help us reuse code across multiple classes. This trait will contain the logic for Uploading the file, Renaming the File, Storing the Files in User Specified Folders, and Delete Files from storage
For example, in an e-commerce system, we may want to separate Images into Folders; Main Images, Secondary Images, Banners, etc.
To do so we will use Illuminate\Http\UploadedFile class to work with the files.
We can also make the trait to be usable with any external disk such as Amazon S3.
Laravel supports local file storage and Amazon S3 out of the box. If you want to use Google Cloud Storage as your storage then you can consider using this package.
Either way, whichever storage service you decide to use, the trait should be able to handle it without adding any extra implementations.
We can create an App\Traits folder in the App directory which will contain all our traits.
<?php
namespace App\Traits;
use Illuminate\Support\Str;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
trait Upload
{
public function UploadFile(UploadedFile $file, $folder = null, $disk = 'public', $filename = null)
{
$FileName = !is_null($filename) ? $filename : Str::random(10);
return $file->storeAs(
$folder,
$FileName . "." . $file->getClientOriginalExtension(),
$disk
);
}
public function deleteFile($path, $disk = 'public')
{
Storage::disk($disk)->delete($path);
}
}
To compress the images upon upload, we can use one of Spatie’s Packages
composer require spatie/laravel-image-optimizer
We can have the images compressed automatically when they are uploaded thereby reducing their file size before uploading them to storage. To set it up, we will first need to register it as route middleware in the app/Http/Kernel.php file.
// app/Http/Kernel.php
protected $routeMiddleware = [
'optimizeImages' => \Spatie\LaravelImageOptimizer\Middlewares\OptimizeImages::class,
];
We will then assign the middleware later to the file upload route.
Step 5: Handle File uploads using a Controller
The next step is to Receive Files through a POST request. We will use the store method in the file upload controller to do so.
We can also check the validations to ensure that the files uploaded are of the correct type and are the allowed files.
The file path is also stored in the file table. The file path is returned from our trait after it has been renamed the file to be uploaded. The format of the file path will be Folder/filename and extension i.e Products/HIDvPbozwW.png
<?php
namespace App\Http\Controllers;
use App\Models\Files;
use App\Traits\Upload; //import the trait
use Illuminate\Http\Request;
class FilesController extends Controller
{
use Upload;//add this trait
public function store(Request $request)
{
if ($request->hasFile('file')) {
$path = $this->UploadFile($request->file('file'), 'Products');//use the method in the trait
Files::create([
'path' => $path
]);
return redirect()->route('files.index')->with('success', 'File Uploaded Successfully');
}
}
}
Step 6: Add the Route
We can add the route to the routes/web.php file. We can use the middleware we created earlier in our route
Route::post('upload-files', [FileController::class,'store'])->middleware('optimizeImages');
Step 7: Add File Upload UI
We can create a view file and add a file upload form
Step 8: Add Storage Link(Optional)
Files uploaded using the public disk option are always stored in the storage/app/public folder. We can use the storage:link command to create a symlink to the public folder.
php artisan storage:link
Step 9: View Uploaded Files
Once the files are uploaded to your preferred storage, you can view the file. If you are using the public folder, you can use the asset helper or public_path helper to return the file path. If you are using a third-party storage system such as S3 or Google Cloud storage, you can use the Storage facade
Using public folder
asset('storage/'.$file->path)
Using S3 or GCS
use Illuminate\Support\Facades\Storage;
Storage::disk('s3')->url($file->path)
You can then append the path as your image source and display them on your application.
<img src="{{asset('storage/'.$file->path)}}" alt="file name">
//or
<img src="{{$file->path}}" alt="file name">
How can I update Files in Laravel?
By default, there is no intrinsic way to update files/images using the default PUT or PATCH request based on this issue. Therefore, we need to look for another way to do so. We could use a POST request but then delete a previous file if it exists and then upload a new file and update the file path in the database.
public function update_file(Request $request) //POST
{
//get the file id and retrieve the file record from the database
$file_id = $request->input('file_id');
$file = Files::where('id', $file_id)->first();
//check if the request has a file
if ($request->hasFile('file')) {
//check if the existing file is present and delete it from the storage
if (!is_null($file->path)) {
$this->deleteFile($file->path);
}
//upload the new file
$path = $this->UploadFile($request->file('file'), 'Products');
}
//upadate the file path in the database
$file->update(['path' => $path]);
//redirect with the success message
return redirect()->back()->with('success', 'File Updated Successfully');
}
We can then define the route to update the file.
Route::post('update-file', [FilesController::class, 'update_file'])->middleware('optimizeImages');
How do I upload multiple Files and Images in Laravel?
It is pretty easy. We can reuse our trait and use a foreach loop to upload and store the path in the database.
We will first update the File Controller to allow for multiple files.
<?php
namespace App\Http\Controllers;
use App\Models\Files;
use App\Traits\Upload;
use Illuminate\Http\Request;
class FilesController extends Controller
{
use Upload;
public function store(Request $request)
{
$file_details = [];
//check if request has files
if ($request->hasFile('files')) {
// loop through each file and upload it
foreach ($request->file('files') as $key => $file) {
//Upload to Storage
$path = $this->UploadFile($file, 'Products');
//reformat the file details
array_push($file_details, [
'path' => $path,
]);
}
//add each file details to database
foreach ($file_details as $key => $value) {
Files::create($value);
}
//clear the file details array
$file_details = [];
}
}
}
The store method now allows for multiple files to be uploaded.
How do I resize files in Laravel?
You can resize files before storing them using the Intervention Image package. This package can help you resize images before storing them.
use Intervention\Image\Facades\Image;
Image::make($file->path)->resize(200, 200);
Conclusion
Working with files and images can be a daunting task in any application but Laravel makes it easy to work with multiple Storage services using their inbuilt APIs and other third-party packages. Thank you for reading