Создание на лету превью изображений (thumbnails)


Сейчас очень много всякого рода плагинов галерей и так далее, но не во всех CRM есть удобные модули (плагины) что бы удобно загружать изображения. Или просто человек который заполняет сайт не утруждается оптимизацией изображений (заливает как есть) и утяжеляет страницу.

Для одной задачи понадобилось изменять размер изображений на лету для вывода превью картинки. Нашел хорошее решение на одном из форумов. Спасибо автору и сохраню у себя на память.

И так:

Если нам необходимо изменить размер изображения мы просто указываем необходимые параметры в адресе изображения.

... src="/images/image.jpg?w=250&h=250" ...

Все остальное за нас делает простой класс на PHP :

class Thumbnail
{
    private $w;
    private $h;
    private $filename;
    private $crop;
    
    private $_mime_settings;
    private $_fsave_allowed;
    private $_tname_tpl         = '%s_%sx%s';
    private $_default_width     = 250;
    private $_default_height    = 250;
    private $_jpeg_quality      = 75;
    private $_sess_varname      = 'THUMB';
       
    public function __construct()
    {
        session_start();
        
        $this->w = abs((int)@$_GET['w']);
        $this->h = abs((int)@$_GET['h']);
        if (!$this->w && !$this->h) {
            # вписать в рамку по умолчанию
            $this->w = $this->_default_width;
            $this->h = $this->_default_height;
        }
        $this->filename = @$_GET['name'];
        $this->crop = isset($_GET['c']) || isset($_GET['tc']);
        
        $this->_mime_settings = array(
            'image/gif'  => array(
                'ext'       => '.gif',
                'create'    => 'imagecreatefromgif',
                'save'      => array(&$this, '_gif_save'),
            ),
            'image/jpeg'  => array(
                'ext'       => '.jpg',
                'create'    => 'imagecreatefromjpeg',
                'save'      => array(&$this, '_jpeg_save'),
            ),
            'image/pjpeg'  => array(
                'ext'       => '.jpg',
                'create'    => 'imagecreatefromjpeg',
                'save'      => array(&$this, '_jpeg_save'),
            ),
            'image/png'  => array(
                'ext'       => '.png',
                'create'    => 'imagecreatefrompng',
                'save'      => array(&$this, '_png_save'),
            ),
        );
        
        $this->_fsave_allowed = isset($_SESSION[$this->_sess_varname]);      
        $this->_run();
    }

    private function _run()
    {
        if (!file_exists($this->filename) || !is_file($this->filename)) exit;
        $info = getimagesize($this->filename);
        if (!$info || !isset($this->_mime_settings[$info['mime']])) {
            # можно возвращать дефолтную картинку
            # .. и удалять лишние картинки
            #$files = glob("{$name}_*{$ext}");
            #glob("*.txt")
            exit;
        }
        $settings =& $this->_mime_settings[$info['mime']];

        $orig_width  = $info[0];
        $orig_height = $info[1];
        $dst_x = $dst_y = 0;
        
        if (!$this->w) {
            # вписываем по высоте
            $new_width  = $this->w = floor($orig_width * $this->h / $orig_height);
            $new_height = $this->h;
        }
        elseif (!$this->h) {
            # вписываем по ширине
            $new_width  = $this->w;
            $new_height = $this->h = floor($orig_height * $this->w / $orig_width);
        }
        elseif ($this->crop) {
            # вписываем с обрезкой
            $scaleW = $this->w / $orig_width;
            $scaleH = $this->h / $orig_height;
            $scale = max($scaleW, $scaleH);
            $new_width  = floor($orig_width * $scale);
            $new_height = floor($orig_height * $scale);
            $dst_x = floor(($this->w - $new_width) / 2);
            $dst_y = floor(($this->h - $new_height) / 2);
        }
        else {
            # вписываем без обрезки
            $scaleW = $this->w / $orig_width;
            $scaleH = $this->h / $orig_height;
            $scale = min($scaleW, $scaleH);
            $new_width  = $this->w = floor($orig_width * $scale);
            $new_height = $this->h = floor($orig_height * $scale);
        }
        
        if ($this->w > $orig_width || $this->h > $orig_height) {
            header('Content-type: ' . $info['mime']);
            readfile($this->filename);
            exit;
        }
        
        $thumbFilename = dirname($this->filename) . '/' 
            . sprintf($this->_tname_tpl, basename($this->filename, $settings['ext']), $this->w, $this->h)
            . $settings['ext']
        ;
        
        if (file_exists($thumbFilename) && filemtime($thumbFilename) >= filemtime($this->filename)) {
            header('Content-type: ' . $info['mime']);
            readfile($thumbFilename);
            exit;
        }
        
        $orig_img = call_user_func($settings['create'], $this->filename);
        $tmp_img  = imagecreatetruecolor($this->w, $this->h);
        # Copy and resize old image into new image
        imagecopyresampled(
            $tmp_img, $orig_img, 
            $dst_x, $dst_y, 
            0, 0, 
            $new_width, $new_height, 
            $orig_width, $orig_height
        );
        imagedestroy($orig_img);
        header('Content-type: ' . $info['mime']);
        call_user_func($settings['save'], $tmp_img, $thumbFilename);
        imagedestroy($tmp_img);
        exit;
    }
    
    private function _gif_save($img, $filename = false)
    {
        if ($filename !== false && $this->_fsave_allowed) imagegif($img, $filename);
	imagegif($img);
    }
    
    private function _jpeg_save($img, $filename = false)
    {
        if ($filename !== false && $this->_fsave_allowed) imagejpeg($img, $filename, $this->_jpeg_quality);
	imagejpeg($img, null, $this->_jpeg_quality);
    }
    
    private function _png_save($img, $filename = false)
    {
        if ($filename !== false && $this->_fsave_allowed) imagepng($img, $filename);
	imagepng($img);
    }
    
}
new Thumbnail;

И так что бы наш класс заработал, сохраним его например в каталог includes в файл class.Thumbnail.php и добавим пару строк в .htaccess:

RewriteEngine On

RewriteRule ^includes/class.Thumbnail.php$ includes/class.Thumbnail.php [L]
RewriteCond %{QUERY_STRING} w=[0-9]+ [OR]
RewriteCond %{QUERY_STRING} h=[0-9]+ [OR]
RewriteCond %{QUERY_STRING} ^tc?$
RewriteRule ^(.*)\.(jpg|gif|png) includes/class.Thumbnail.php?name=%{REQUEST_FILENAME}&%{QUERY_STRING} [NC,L]

А вот если у вас стоит связка NGINX + APACHE и nginx настрое на отдачу статики то или вы убираете из правила где отдается статика jpg|gif|png или в это правило вставляете:

rewrite ^/includes/class.Thumbnail.php$ /includes/class.Thumbnail.php last;
if ($args ~ "w=[0-9]+"){
	set $rule_1 1;
}
if ($args ~ "h=[0-9]+"){
	set $rule_1 1;
}
if ($args ~ "^tc?$"){
	set $rule_1 1;
}
if ($rule_1 = "1"){
	rewrite ^/(.*).(jpg|gif|png) /includes/class.Thumbnail.php?name=$request_filename&$args last;
}

Параметры GET для работы с изображением:

?[w=xx][h=xx][c]|[t[c]]

w (width) - ширина, в которую надо вписать

h (height) - высота, в которую надо вписать

Если указан только один параметр, то сделает по нему, например картинку надо вписать в высоту 100, тогда пишем ?h=100 и все.
Если вписать без обрезки в рамку, например 150х100, то пишем ?w=150&h=100 --- впишет по максимальной подходящей размерности

Если нужно "жестко" вписать в рамку, например сделать картинку 100х100, чтоб заполняла все пространство (обрезка), то пишем ?w=100&h=100&c

?t - вписывание в рамку по умолчанию

?tc - обрезка по рамке с размерами по умолчанию

Так же есть вариант записи на диск:
Запись превью на диск разрешается, если в сессии определена переменная THUMB

Данное решение работает на GD, зато простое и работает, и все что остается после настройки так это внести изменения в url изображений

#php #htaccess #nginx

Copyright © 2019