1
2

More than 3 years have passed since last update.

画像の動的resize on Laravel

Posted at
<img src="/path/to/img.jpg" >

って書いてて、あなんかこの画像やっぱ重いなぁと思ったら

<img src="{{ resize('/path/to/img.jpg' ,200, 200) }}" >
<!-- /storage/cache/aerwsvv4w4cey75hf.jpg のように置換されます-->

って書き換えるだけで

  • サムネイル生成(初回アクセス時のみ)
  • 2回目以後のアクセスは、キャッシュ画像に直接アクセス
  • サムネイルは、画像の更新や指定サイズの変更タイミングで自動更新

される実装です。

command
$ composer require intervention/image
$ php artisan storage:link
$ mkdir storage/app/public/cache
DynamicImage.php
<?php

namespace App\Services;

use Intervention\Image\Image;

/**
 * Class DynamicThumb
 * @package App\Services
 */
class DynamicImage
{
    const CACHE_DIR = 'cache';
    const IMG_QUALITY = 80;

    /**
     * resize処理
     * @param string $imagePath
     * @param array $query
     * @return string|null
     */
    public static function resize(string $imagePath, array $query): string
    {
        return self::execute($imagePath, __METHOD__, $query);
    }

    /**
     * crop処理
     * @param string $imagePath
     * @param array $query
     * @return string|null
     */
    public static function crop(string $imagePath, array $query): string
    {
        return self::execute($imagePath, __METHOD__, $query);
    }

    /**
     * fit処理
     * @param string $imagePath
     * @param array $query
     * @return string|null
     */
    public static function fit(string $imagePath, array $query): string
    {
        return self::execute($imagePath, __METHOD__, $query);
    }

    /**
     * キャッシュがなければ生成して publicパスを返す
     * @param string $imagePath
     * @param string $action
     * @param array $query
     * @return string|null
     */
    private static function execute(string $imagePath, string $action, array $query): ?string
    {
        if (!self::cacheExists($imagePath, $query)) {

            $image = \Image::make(public_path($imagePath));

            $action = $action . 'Image';
            $image = $action($image, $query);

            $image->save(self::getFullPathOfCacheImage($imagePath, $query), self::IMG_QUALITY);
        }

        return self::getPublicPathOfCacheImage($imagePath, $query);
    }

    /**
     * キャッシュファイル存在するか?
     * @param string $imagePath
     * @param array $query
     * @return bool
     */
    private static function cacheExists(string $imagePath, array $query): bool
    {
        return file_exists(self::getFullPathOfCacheImage($imagePath, $query));
    }

    /**
     * キャッシュディレクトリのpublicパス
     * @return string
     */
    private static function getPublicPathOfCacheDir(): string
    {
        return '/storage/' . self::CACHE_DIR . '/';
    }

    /**
     * キャッシュディレクトリのフルパス
     * @return string
     */
    private static function getFullPathOfCacheDir(): string
    {
        return storage_path('app/public/' . self::CACHE_DIR) . '/';
    }

    /**
     * キャッシュ画像のbaseName
     * @param string $imagePath
     * @param array $query
     * @return string
     */
    private static function getBaseNameOfCacheImage(string $imagePath, array $query): string
    {
        return md5(http_build_query([
            'path' => $imagePath,
            'query' => $query,
            'modified' => filemtime(public_path($imagePath))
        ])) . '.jpg';
    }

    /**
     * キャッシュ画像のpublicパス
     * @param string $imagePath
     * @param array $query
     * @return string
     */
    private static function getPublicPathOfCacheImage(string $imagePath, array $query): string
    {
        return self::getPublicPathOfCacheDir() . self::getBaseNameOfCacheImage($imagePath, $query);
    }

    /**
     * キャッシュ画像のフルパス
     * @param string $imagePath
     * @param array $query
     * @return string
     */
    private static function getFullPathOfCacheImage(string $imagePath, array $query): string
    {
        return self::getFullPathOfCacheDir() . self::getBaseNameOfCacheImage($imagePath, $query);
    }

    /**
     * $width x $height に収まるよう縮小
     * @param Image $image
     * @param array $query
     * @return Image
     */
    private static function resizeImage(Image $image, array $query): Image
    {
        $width = $height = null;
        extract($query);

        if ($image->width() < $image->height()) {
            $image->resize($width, null, function ($constraint) {
                $constraint->aspectRatio();
                $constraint->upsize();
            });
        } else {
            $image->resize(null, $height, function ($constraint) {
                $constraint->aspectRatio();
                $constraint->upsize();
            });
        }

        return $image;
    }

    /**
     * $position(top, center ,bottom)を起点に、$width x $height に収まるようトリミング
     * @param Image $image
     * @param array $query
     * @return Image
     */
    private static function fitImage(Image $image, array $query): Image
    {
        $width = $height = $position = null;
        extract($query);

        $image->fit($width, $height, function ($constraint) {
            $constraint->upsize();
        }, $position ?? 'center');

        return $image;
    }

    /**
     * $x x $yを起点に $width x $height で切り出す
     * @param Image $image
     * @param array $query
     * @return Image
     */
    private static function cropImage(Image $image, array $query): Image
    {
        $width = $height = $x = $y = null;
        extract($query);

        if ($x) {
            $image->crop($width, $height, $x, $y);
        } else {
            $image->crop($width, $height);
        }

        return $image;
    }
}
helpers.php
<?php

use App\Services\DynamicImage;


if(!function_exists('resize')){

    function resize(string $basePath, int $width, int $height): string
    {
        return DynamicImage::resize($basePath, compact('width', 'height'));
    }
}

if(!function_exists('crop')){

    function crop(string $basePath, int $width, int $height, ?int $x = null, ?int $y = null): string
    {
        return DynamicImage::crop($basePath, compact('width', 'height', 'x', 'y'));
    }
}

if(!function_exists('fit')){

    function fit(string $basePath, int $width, int $height, ?string $position = null): string
    {
        return DynamicImage::fit($basePath, compact('width', 'height', 'position'));
    }
}



1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2