Smooth image upload and resize in laravel

I find myself adding image upload and resize feature in most of the app I build. Laravel comes already packed with very easy file upload, but things get complicated as soon we need to manipulate the uploaded image. In this post, I will show you how you can make uploading and resizing image process buttery smooth.

A typical app has some sort of controller where it checks if the request has a file to upload. After uploading we need to resize it and then clean up old files and update the database with the new uploaded image path. A simple solution will be something like this:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Intervention\Image\Facades\Image;

class UserAvatarController extends Controller
{
    public function store(Request $request)
    {
        // validate the image
        $this->validate($request, [
            'avatar' => 'required|image|max:2000'
        ]);

        // get the user
        $user = auth()->user();

        // upload and resize using Intervention Image 
        $filename = 'uploads/avatar-'.$user->id.'.jpg';

        Image::make($request->file('avatar'))
            ->fit(200, 200)
            ->save($filename, 80);

        // update model
        $user = auth()->user();
        $oldAvatar = $user->avatar;
        $user->update(['avatar' => $filename]);

        // delete old image
        unlink($oldAvatar);

        return [
            'avatar' => asset($filename)
        ];
    }
}

Now it’s not that bad but we can do better. We will be using qcod/laravel-imageup to make this process smooth.

The qcod/laravel-imageup is a trait which gives you auto upload, resize and crop for image feature with tons of customization.

Create a new Laravel 5.7 app and get started.

Installation

You can install the package via composer:

composer require qcod/laravel-imageup

The package will automatically register itself. In case you need to manually register it you can by adding it in config/app.php providers array:

QCod\ImageUp\ImageUpServiceProvider::class

You can optionally publish the config file with:

php artisan vendor:publish --provider="QCod\ImageUp\ImageUpServiceProvider" --tag="config"

It will create config/imageup.php with all the settings you can customize.

Configuration

Now we have the package, let’s hook it with our User model to handle the avatar uploading functionality.

use QCod\ImageUp\HasImageUploads;

class User extends Authenticatable
{
    use Notifiable, HasImageUploads;

    /**
     * All the images fields for model
     *
     * @var array
     */
    protected static $imageFields = [
        'avatar' => [
            'placeholder' => 'https://api.adorable.io/avatars/160',
            'width' => 160,
            'height' => 160,
            'resize_image_quality' => 90,
            'crop' => true
        ]
    ];

    //...
}

That’s it, to give you upload functionality. Now in our UserController you can just make a request with the image file to upload and resize and do all the things we have done earlier automatically in just 2 line of code.

public function store(Request $request) 
{
    $user = auth()->user();
    $user->update($request->all());

    return $user;
}

As soon Model::saved() event fired this will check for any file named avatar in the $request. If it’s find it, it will auto upload and crop it based on the configuration we have provided in User model $imageFields field definition.

If you look into the $imageFields array of the User model, you can see we have provided  ['width' => 160, 'height' => 160, 'crop' => true]. Which tells to crop the image on that size. If you omit the crop option image will be resized by keeping the aspect ratio of the image.

Manually Uploading and Resizing Image

That’s great to auto upload and in most of the cases that will be enough. But imagine you need to upload the image and crop it manually. The image file can be available already on server filesystem or it’s coming from a user request.

Make sure you have disabled auto upload by setting protected $autoUploadImages = false; on model or by calling $model->setImagesField(['avatar' => ['auto_upload' => false]); otherwise you will be not seeing your manual uploads, since it will be overwritten by auto upload upon model save.

Let’s see how easy it is to manually upload and resizing an image.

$user = User::findOrFail($id);

$user->uploadImage(request()->file('avatar'), 'avatar');
// or 
$user->uploadImage(request()->file('avatar'));

Upload image for given $field, if $field is null it will upload to first image option defined on the Models imageFields array.

Resize

If you have an image already you can call this method to resize it with the same options we have used for image fields.

$user = User::findOrFail($id);

// resize image, it will give you resized image, you need to save it  
$imageFile = '/images/some-big-image.jpg';
$image = $user->resizeImage($imageFile, [ 'width' => 120, 'crop' => true ]);

// or you can use uploaded file
$imageFile = request()->file('avatar');
$image = $user->resizeImage($imageFile, [ 'width' => 120, 'crop' => true ]);

Crop Image

You can use this cropTo($x, $y) method to set the x and y coordinates of cropping. It will be very useful if you are getting coordinate from some sort of front-end image cropping library like CropperJs.

$user = User::findOrFail($id);

// uploaded file from request
$imageFile = request()->file('avatar');

// coordinates from request
$coords = request()->only(['crop_x', 'crop_y']);

// resizing will give you intervention image back
$image = $user->cropTo($coords)
    ->resizeImage($imageFile, [ 'width' => 120, 'crop' => true ]);

// or you can do upload and resize like this, it will override field options crop setting
$user->cropTo($coords)
    ->uploadImage(request()->file('cover'), 'avatar');

As you can see It gives you tons of customization and you can manipulate your image any way you want, Thanks to awesome Intervention Image library which it uses under the hood.

Laravel ImageUp customization options

Laravel ImageUp allows you to customize how the upload and resize will be handled from defined field options, following are the things you can customize:

<?php
namespace App;

use QCod\ImageUp\HasImageUploads;
use Illuminate\Database\Eloquent\Model;

class User extends Model {
    
    use HasImageUploads;
    
    // which disk to use for upload, can be override by field options 
    protected $imagesUploadDisk = 'local';
    
    // path in disk to use for upload, can be override by field options 
    protected $imagesUploadPath = 'uploads';
    
    // auto upload allowed 
    protected $autoUploadImages = true;
    
    // all the images fields for model
    protected static $imageFields = [
        'avatar' => [
            // width to resize image after upload
            'width' => 200,
            
            // height to resize image after upload
            'height' => 100,
            
            // set true to crop image with the given width/height and you can also pass arr [x,y] coordinate for crop.
            'crop' => true,
            
            // what disk you want to upload, default config('imageup.upload_disk')
            'disk' => 'public',
            
            // a folder path on the above disk, default config('imageup.upload_directory')
            'path' => 'avatars',
            
            // placeholder image if image field is empty
            'placeholder' => '/images/avatar-placeholder.svg',
            
            // validation rules when uploading image
            'rules' => 'image|max:2000',
            
            // override global auto upload setting coming from config('imageup.auto_upload_images')
            'auto_upload' => false,
            
            // if request file is don't have same name, default will be the field name
            'file_input' => 'photo'
        ],
        'cover' => [
            //...    
        ]
    ];
}

Conclusion

Although I have tried to cover most of the feature for qcod/laravel-imageup but if you need any help with this don’t forget the check the github docs for this. I hope it helps you. If you find any issue in package please open an issue I will fix them asap. Happy coding 😎