DEV Community

Ian Kumu
Ian Kumu

Posted on • Originally published at iankumu.com on

Laravel File Upload: A Complete Tutorial and Guide

Working with files in any application is a crucial part of any system. Laravel provides a filesystem from where we can interact with files; file upload, storing or retrieving them.

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. 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 
Enter fullscreen mode Exit fullscreen mode

Or

composer create-project laravel/laravel test
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'];
}

Enter fullscreen mode Exit fullscreen mode

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');
    }
};

Enter fullscreen mode Exit fullscreen mode

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);
    }
}

Enter fullscreen mode Exit fullscreen mode

To compress the images upon upload, we can use one of Spatie’s Packages

composer require spatie/laravel-image-optimizer 
Enter fullscreen mode Exit fullscreen mode

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,   
   ]; 
Enter fullscreen mode Exit fullscreen mode

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 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;
use Illuminate\Http\Request;

class FilesController extends Controller
{
    use Upload;

    public function store(Request $request)
    {
        if ($request->hasFile('file')) {
            $path = $this->UploadFile($request->file('file'), 'Products');
            Files::create([
                'path' => $path
            ]);
            return redirect()->route('files.index')->with('success', 'File Uploaded Successfully');
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Laravel File upload
File paths in the database

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'); 
Enter fullscreen mode Exit fullscreen mode

Step 7: Add File Upload UI

We can create a view file and add a file upload form

Laravel File Upload Form
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 
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Using S3 or GCS

use Illuminate\Support\Facades\Storage;
Storage::disk('s3')->url($file->path)
Enter fullscreen mode Exit fullscreen mode

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">

Enter fullscreen mode Exit fullscreen mode

Laravel File Upload
Display Files

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 onthis 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');
    }
Enter fullscreen mode Exit fullscreen mode

We can then define the route to update the file.

Route::post('update-file', [FilesController::class, 'update_file'])->middleware('optimizeImages');

Enter fullscreen mode Exit fullscreen mode

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);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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.

If you enjoyed this article, you'd love the Free Laravel Guide I prepared for you. Be sure to get your guide today.
Thank you for reading

The post Laravel File Upload: A Complete Tutorial and Guide appeared first on Ian Kumu's Blog.

Top comments (0)