Upload, resize and save images using Intervention and AWS S3

Uploading images is one thing but being able to modify them is very useful, especially when it comes to resizing and reducing filesizes. Cue Intervention Image.

Intervention Image is an open source PHP image handling and manipulation library. It provides an easier and expressive way to create, edit, and compose images and supports currently the two most common image processing libraries GD Library and Imagick.

Install Intervention Image (Laravel Method)

You can follow the docs for this also, but i'll list the steps here too.

compose require intervention/image

Once composer has pulled in the files, open up your config/app.php file and copy over the ImageServiceProvider as the last entry of the providers[] array under:

/* * Laravel Framework Service Providers... */
...
Intervention\Image\ImageServiceProvider::class

Then further down under the aliases[] array add the following facade:

'Image' => Intervention\Image\Facades\Image::class

That's all there is to the installation. Laravel will handle autoloading the necessary files.

Uploading, Resizing and Storing on AWS S3

I'll use a very simple example form with a file input to get us going. We'll need a new blade file. I've saved mine as resources/views/index.blade.php, but you can save it wherever you please.

/* resources/views/index.blade.php */
<form method="POST" action="/store" enctype="multipart/form-data"><code>
    @csrf
    <div class="form-group">
        <label for="image">Feature Image</label>
        <input type="file" name="image" class="form-control" required accept="image/png, image/jpeg">
    </div>
    <button type="submit" class="mt-4 btn btn-rounded btn-primary">Upload Image</button>
</form>

A handy feature of the file input is that you can specify an accept="image/png, image/jpeg" property which restricts what type of file a user can upload. Your can replace these with the files you require, just make sure to use the correct MIME type for the file.

We need a controller to handle the file upload part, but before that we need to specify our routes in the web.php file.

Route::get('/', 'App\Http\Controllers\ImageController@index')->name('home');
Route::post('/store', 'App\Http\Controllers\ImageController@store');

Next lets create a controller to handle the image upload and resize. In your terminal, fire off the following command: php artisan controller:create ImageController.

This will create a file in: App\Http\Controllers\ImageController.php

Add the following:


namespace App\Http\Controllers;

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

public function index()
{
    return view('index');
}

public function store(Request $request)
{
    $request->validate([
        'image' => ['required', 'image', 'max:4028']    
    ]);
    
    $filePath = '/images/ . $request->file('image')->hashName();

    $image = Image::make($request->file('image'))->resize(368, null, function ($constraint) {
        $constraint->aspectRatio();
        $constraint->upsize();
    })->encode('jpg', 60);

    Storage::disk('s3')->put($filePath, $image->stream(), 'public');

    $request->merge([
        'logo' => $filePath,
    ]);

    $user = User::create($request->only([
        'logo'    
    ]));

    return redirect('home')->with('success_message', 'Image Uploaded Successfully.');
}

Let's break down the above code. The index function is straightforward, it just returns the view where you'd display your form.

In the store method we validate if an image was submitted and that it doesn't exceed as maximum size (around 4MB in this example).

Next we prepare the file path / file name. $filePath = '/images/ . $request->file('image')->hashName(); You can change this to suit your needs. A personal preference of mine is to use the hashname of the image instead of the actual filename, but if you want to use the original name, try $request->file('image')-getClientOriginalName();. Or hardcode a filename instead.

Moving on to the image resizing part, this is where Intervention Image comes into play. Intervention allows you to resize an image whilst also maintaining the aspect ratio. You can even chose what file format and quality to save the image as. Popular choices are JPEG and PNG.

Uploading the file is made very easy. Using the Storage Facade you can choose a disk to save your files to. Think of a disk as place or service which can store files. In the example the Storage::disk('s3')->put($filePath, $image->stream(), 'public'); method on the facade accepts 3 arguments:

  1. The path you want the file to be saved to
  2. The uploaded image or a file stream object
  3. The visibility of the file
  4. When using S3 or Digital Ocean Spaces you can specifiy public or private.

Finally we merge the image back into the $request object. At this point you might want to save the path of the image to a model of your choice. Set the request object to match the field in your database.

As a side note, you'll need to have an AWS user with programtical access to S3 and a S3 bucket setup to attempt this. Once you have these details edit your .env file and update accordingly.

AWS_ACCESS_KEY_ID=YOUR_KEY
AWS_SECRET_ACCESS_KEY=YOUR_SECRET
AWS_DEFAULT_REGION=YOUR_REGION
AWS_BUCKET=YOUR_BUCKET
AWS_URL=https://your_bucket.s3.your_region.amazonaws.com


More Posts

Laravel PDF API

As part of my day job I was required to make PDF's from HTML templates and expose this via an...