Corbin's avatar

Handling images in a scalable way.

Note I'm going to be using sudo code here.

There isn't much on Laracasts about handling images beyond beginner methods of just storing images to a public directory and storing the path in the DB.

Post example

Post Table

id|title|content|thumbnail

The post controller saves an image when a route hits the store method on the controller:

public function store(Request $request)
    {
        $post = new Post($request->all());

        if ($post->img_path == ! null ) {

            //Gets image type from url
            $info = getimagesize($post->img_path);
            $extension = image_type_to_extension($info[2]);

            // Creates file name
            $filename = str_slug($post->title, '-') . time() . $extension;

            //Makes image
            Image::make($post->img_path)->save(public_path('images/post-thumbnails/'.$filename));
           
        //Saves new image path to DB
       $post->img_path = $filename;
        }
        $post->save();

    }

The problem is none of that is scalable. First I'm not taking advantage of the Laravel file storage system. This becomes problematic when wanting to swap from storing on your server to a CDN like S3.

The second problem is that it becomes hard to have one plan for how to deal with responsive images. Using just CSS can lead to scaling an image down which will use a lot of bandwidth.

It seems like there are a few methods you can use to deal with this. For example you could use srcset on image tags and render a different image for each media breaking point:

First Method

<img srcset="public/images/thumbnail-576.jpg 1x, public/images/thumbnail-768jpg 2x " alt="…

And then use a one to many relationship with post and a thumbnails table for each media breaking point:

Tailwind breaking points

  • 576px
  • 768px
  • 992px
  • 1200px;

Post Table

id|title|content|

Tumbnail Table

|post_id|thumbnail_path

PostController

public function store(Request $request)
{
    $post = new Post($request->all());

    if ($post->thumbnail == ! null ) {

        //Gets image type from url
        $info = getimagesize($post->thumbnail);
        $extension = image_type_to_extension($info[2]);

        // Creates file name
        $filename = str_slug($post->title, '-') . time() . $extension;

        //Makes image
        $image = Image::make($post->thumbnail)

        $image->save(public_path('images/post-thumbnails/'.$filename));
        $image->resize(576, null)->save(public_path('images/post-thumbnails/576-'.$filename));
        $image->resize(768, null)->save(public_path('images/post-thumbnails/768-'.$filename));
        $image->resize(992, null)->save(public_path('images/post-thumbnails/992-'.$filename));
        $image->resize(1200, null)->save(public_path('images/post-thumbnails/1200-'.$filename));


   
        //Saves new image path to DB
       
        Thumbnail:Create::(['post_id'=>$post->id, 'thumbnail_path'=> '576-'.$filename]);
        Thumbnail:Create::(['post_id'=>$post->id, 'thumbnail_path'=> '768-'.$filename]);
        Thumbnail:Create::(['post_id'=>$post->id, 'thumbnail_path'=> '992-'.$filename]);
        Thumbnail:Create::(['post_id'=>$post->id, 'thumbnail_path'=> '1200-'.$filename]);
    }
    
    $post->save();

}

Note: I haven't started using the laravel filesytem yet

Second method

Is just store each image into their own directory:

$image = Image::make($post->thumbnail)

        $image->save(public_path('images/post-thumbnails/'.$filename));
        $image->resize(576, null)->save(public_path('images/post-thumbnails/576/'.$filename));
        $image->resize(768, null)->save(public_path('images/post-thumbnails/768/'.$filename));
        $image->resize(992, null)->save(public_path('images/post-thumbnails/992/'.$filename));
        $image->resize(1200, null)->save(public_path('images/post-thumbnails/1200/'.$filename));

Third method

The last option is to do it on-demand, by routing the image URLs to a script that dissects the URL path, finds the right image, scales it on the fly, and serves the scaled version from memory.

I have no idea where to even begin with this method.

I would just absolutely love if there was a guide or Laravel lessons diving deeper into scalability like this. More specifically images, the file system, and responsive image output. I'm a total noob when it comes to this stuff.

0 likes
2 replies
jlrdw's avatar

Using just CSS can lead to scaling an image down which will use a lot of bandwidth.

That's where pagination comes in. On a site I did there are images uploaded properly optimized for web. And with css media queries I displayed smaller for a mobile. But a click on image would show image larger of course.

You should be fine if you don't attempt to have 100 images on a page. I'd say 5 and no more than 10. The more traffic the less per page.

But yes you could have more than one folder with various sized images, I never have.

And use a good package to get proper rotation and optimization.

Don't attempt professional uploading images yourself or users.

If you do you will be in for a mess later on, some turned sidways, well you'll see.

At humane society it was in house and Adobe Photoshop was used for well optimized images.

And I hope I did not misunderstand the intent of your question.

Snapey's avatar
Snapey
Best Answer
Level 122

dont overlook spatie media manager. it will solve most of your concerns and also deal with responsive and progressive images

1 like

Please or to participate in this conversation.