Постепенно скрипт модернизировался, на данный момент постарался сделать его максимально удобным и быстрым в подключении к любому сайту. Основные возможности:
— ресайз jpg, png, gif;
— возможность задания ширины и высоты сжатого изображения либо только одного из параметров, второй будет подсчитан пропорционально от исходного изображения;
— три вида ресайзинга (добавление полос — определенного цвета или прозрачных, обрезка изображения);
— кеширование обработанных картинок.
Примеры и код под катом.
Для работы ресайзера используется собственно сам скрипт и файл .htaccess для более простого задания адресов картинок. В принципе, можно обойтись и без последнего, но тогда атрибут src у изображений будет несколько страшным.
В случае использования htaccess вызов ресайзера выглядит следующим образом:
image/<resize_type>/<new_width>/[new_height]/<img_src>
Где:
- resize_type — обязательный параметр, принимает значения b — borders, t — transparent, c — crop;
- new_width — обязательный параметр, ширина нового изображения в пикселах. Если указан 0 — посчитает ширину изображения автоматически, исходя из высоты нового изображения с сохранением пропорций. В этом случае, поскольку полученная картинка будет полностью пропорциональна исходной выбор вида ресайзинга не играет никакой роли. Использование нуля актуально для горизонтальных слайдеров, высота которых должна быть фиксирована;
- new_height — необязательный параметр высоты нового изображения. Если его не указать или указать 0 — посчитает автоматически с сохранением пропорций;
- img_src — путь к картинке.
Собственно, примеры работы в различных режимах с одним и тем же исходным изображением. Размер исходной картинки — 450х411 пикселей.
Ресайз с добавлением полос к размеру 300х200 (image/b/300/200/img_1.jpg):
Работа ресайзера в данном случае простая. Сначала определяется, какая из сторон имеет больший коэффициент сжатия: 450/300 = 1.5 и 411/200 = 2.055. Затем изображение уменьшается в 2.055 раза по обоим параметрам, в результате чего получаем картинку в размере 219х200 и добавляются полосы слева и справа до заданных 300 пикселей. Аналогичным образом будет выглядеть и использование параметра t, так как формат сохраняемого в кеше изображения совпадает с исходным, а правильное его отображение (с прозрачностью) достигается за счет передачи header'ов для png изображений.
Ресайз с пропорциональным обрезанием к размеру 300х200 (image/c/300/200/img_1.jpg):
В данном случае будет выбран минимальный из коэффициентов сжатия, в данном случае — 1.5. Затем изображение уменьшается в 1.5 раза по обоим параметрам, что дает на выходе 300х274, а затем лишние 74 будут обрезаны — по 37 сверху и снизу.
Если не указать параметр высоты, оставив только ширину, то она будет рассчитана автоматически. К примеру, для ширины 300 — это 2/3 от ширины исходного изображения, значит и новая высота должна составить 2/3 от исходных 411, а конкретно 274 (image/b/300/img_1.jpg):
Аналогичным образом, если указать ширину равной нулю, то получим её автоматический расчет (image/b/0/300/img_1.jpg):
<?
error_reporting( 0 );
//цвет "полос" по бокам
$color = array( 255, 255, 255 );
$file_name = $_GET["src"];
list( $width, $height, $ext ) = getimagesize( $file_name );
//задаем новые ширину и высоту
$params["width"] = (int)$_GET["w"];
if( !isset( $_GET["h"] ) || 0 == (int)$_GET["h"] ){
$params["height"] = $height * $params["width"]/$width;
} else {
$params["height"] = (int)$_GET["h"];
}
if( 0 == $params["width"] ){
$params["width"] = $width * $params["height"]/$height;
}
//папка с кешем изображений. Внутри img_cache/ повторяется полный путь исходного изображения
$cache_file_folder = "img_cache/" . substr( $file_name, 0, strrpos( $file_name, "/" ) );
$cache_file_name = "img_cache/" . substr( $file_name, 0, strrpos( $file_name, "." ) ) . "_" . $_GET["resize_type"] . "_" . $params["width"] . "_" . $params["height"] . substr( $file_name, strrpos( $file_name, "." ) );
//проверка наличия изображения в кеше. Если оно есть и было создано после последнего изменения исходного изображения - берем его
if( file_exists( $cache_file_name ) ){
if( filemtime( $cache_file_name ) > filemtime( $file_name ) ){
if( 1 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_GIF ) );
} else if( 2 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_JPEG ) );
} else if( 3 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_PNG ) );
}
//обновление времени последнего изменения. Используется, если необходима очистка давно не запрашиваемых картинок
touch( $cache_file_name );
echo file_get_contents( $cache_file_name );
exit();
}
}
//пытаемся создать папку для кеша
mkdir( $cache_file_folder, 0755, true );
//непосредственно код ресайзинга
switch( $_GET["resize_type"] ){
case "b":
if( 1 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_GIF ) );
$image = imagecreatefromgif( $file_name );
} else if( 2 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_JPEG ) );
$image = imagecreatefromjpeg( $file_name );
} else if( 3 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_PNG ) );
$image = imagecreatefrompng( $file_name );
}
$new_width = (int)$params["width"];
$new_height = (int)$params["height"];
if( $new_width == 0 || $new_height == 0 || !$image || !in_array( $ext, array( 1, 2, 3 ) ) ){
$image_new = imagecreatetruecolor( 100, 30 );
$bgc = imagecolorallocatealpha( $image_new, $color[0], $color[1], $color[2], 127 );
$tc = imagecolorallocate( $image_new, 0, 0, 0 );
imagefilledrectangle( $image_new, 0, 0, 100, 30, $bgc );
imagestring( $image_new, 1, 5, 5, "Error loading", $tc );
if( 1 == $ext ){
imagegif( $image_new );
imagedestroy( $image_new );
} else if( 2 == $ext ){
imagejpeg( $image_new );
imagedestroy( $image_new );
} else if( 3 == $ext ){
imagepng( $image_new );
imagedestroy( $image_new );
}
exit();
}
$image_new = imagecreatetruecolor( $new_width, $new_height );
imagealphablending( $image_new, false );
imagesavealpha( $image_new, true );
$bgc = imagecolorallocatealpha( $image_new, $color[0], $color[1], $color[2], 127 );
imagefilledrectangle( $image_new, 0, 0, $new_width, $new_height, $bgc );
if( $new_width/$new_height > $width/$height ){
$ins = $width*($new_height/$height);
$out = ( $new_width - $ins )/2;
imagecopyresampled( $image_new, $image, $out, 0, 0, 0, $ins, $new_height, $width, $height );
} else {
$ins = $height*($new_width/$width);
$out = ( $new_height - $ins )/2;
imagecopyresampled( $image_new, $image, 0, $out, 0, 0, $new_width, $ins, $width, $height );
}
if( 1 == $ext ){
imagegif( $image_new );
imagegif( $image_new, $cache_file_name );
imagedestroy( $image_new );
} else if( 2 == $ext ){
imagejpeg( $image_new );
imagejpeg( $image_new, $cache_file_name );
imagedestroy( $image_new );
} else if( 3 == $ext ){
imagepng( $image_new );
imagepng( $image_new, $cache_file_name );
imagedestroy( $image_new );
}
break;
case "t":
if( 1 == $ext ){
$image = imagecreatefromgif( $file_name );
} else if( 2 == $ext ){
$image = imagecreatefromjpeg( $file_name );
} else if( 3 == $ext ){
$image = imagecreatefrompng( $file_name );
}
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_PNG ) );
$new_width = (int)$params["width"];
$new_height = (int)$params["height"];
if( $new_width == 0 || $new_height == 0 || !$image || !in_array( $ext, array( 1, 2, 3 ) ) ){
$image_new = imagecreatetruecolor( 100, 30 );
$bgc = imagecolorallocatealpha( $image_new, $color[0], $color[1], $color[2], 127 );
$tc = imagecolorallocate( $image_new, 0, 0, 0 );
imagefilledrectangle( $image_new, 0, 0, 100, 30, $bgc );
imagestring( $image_new, 1, 5, 5, "Error loading", $tc );
if( 1 == $ext ){
imagegif( $image_new );
imagedestroy( $image_new );
} else if( 2 == $ext ){
imagejpeg( $image_new );
imagedestroy( $image_new );
} else if( 3 == $ext ){
imagepng( $image_new );
imagedestroy( $image_new );
}
exit();
}
$image_new = imagecreatetruecolor( $new_width, $new_height );
imagealphablending( $image_new, false );
imagesavealpha( $image_new, true );
$bgc = imagecolorallocatealpha( $image_new, $color[0], $color[1], $color[2], 127 );
imagefilledrectangle( $image_new, 0, 0, $new_width, $new_height, $bgc );
if( $new_width/$new_height > $width/$height ){
$ins = $width*($new_height/$height);
$out = ( $new_width - $ins )/2;
imagecopyresampled( $image_new, $image, $out, 0, 0, 0, $ins, $new_height, $width, $height );
} else {
$ins = $height*($new_width/$width);
$out = ( $new_height - $ins )/2;
imagecopyresampled( $image_new, $image, 0, $out, 0, 0, $new_width, $ins, $width, $height );
}
imagepng( $image_new );
imagepng( $image_new, $cache_file_name );
imagedestroy( $image_new );
break;
case "c":
if( 1 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_GIF ) );
$image = imagecreatefromgif( $file_name );
} else if( 2 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_JPEG ) );
$image = imagecreatefromjpeg( $file_name );
} else if( 3 == $ext ){
header( "Content-type: " . image_type_to_mime_type( IMAGETYPE_PNG ) );
$image = imagecreatefrompng( $file_name );
}
$new_width = (int)$params["width"];
$new_height = (int)$params["height"];
if( $new_width == 0 || $new_height == 0 || !$image || !in_array( $ext, array( 1, 2, 3 ) ) ){
$image_new = imagecreatetruecolor( 100, 30 );
$bgc = imagecolorallocate( $image_new, 255, 255, 255 );
$tc = imagecolorallocate( $image_new, 0, 0, 0 );
imagefilledrectangle( $image_new, 0, 0, 100, 30, $bgc );
imagestring( $image_new, 1, 5, 5, "Error loading", $tc );
if( 1 == $ext ){
imagegif( $image_new );
imagedestroy( $image_new );
} else if( 2 == $ext ){
imagejpeg( $image_new );
imagedestroy( $image_new );
} else if( 3 == $ext ){
imagepng( $image_new );
imagedestroy( $image_new );
}
exit();
}
$image_new = imagecreatetruecolor( $new_width, $new_height );
imagealphablending( $image_new, false );
imagesavealpha( $image_new, true );
$width/$new_width < $height/$new_height ? $coef = $width/$new_width : $coef = $height/$new_height;
$start_x = ( $width - $new_width*$coef )/2;
$start_y = ( $height - $new_height*$coef )/2;
$end_x = $width - 2*$start_x;
$end_y = $height - 2*$start_y;
imagecopyresampled( $image_new, $image, 0, 0, $start_x, $start_y, $new_width, $new_height, $end_x, $end_y );
if( 1 == $ext ){
imagegif( $image_new );
imagegif( $image_new, $cache_file_name );
imagedestroy( $image_new );
} else if( 2 == $ext ){
imagejpeg( $image_new );
imagejpeg( $image_new, $cache_file_name );
imagedestroy( $image_new );
} else if( 3 == $ext ){
imagepng( $image_new );
imagepng( $image_new, $cache_file_name );
imagedestroy( $image_new );
}
break;
}
.htaccess:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^image/([a-z])/([0-9]+)/([0-9]+)/([A-z0-9-\/\.]+)$ image.php?resize_type=$1&w=$2&h=$3&src=$4 [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^image/([a-z])/([0-9]+)/([A-z0-9-\/\.]+)$ image.php?resize_type=$1&w=$2&src=$4 [QSA,L]
</IfModule>
Спасибо за внимание. Надеюсь, скрипт кому-нибудь да пригодится.
Комментарии ()
IncorrecTSW
12.05.2015 11:39+3Каждый раз, когда кто то пишет такие свичи (код), в мире умирает котенок.
maximw
12.05.2015 11:43-2Без белого списка размеров вам такую DoS устроят, плюс забьют все дисковое пространство.
mkuzmin
12.05.2015 11:47-1github.com/thumbor/thumbor
thumbor is an open-source photo thumbnail service by globo.com
nikitasius
12.05.2015 12:43-2- К чему все это, когда есть convert?
Если ложно то
- К чему хостинг, где нету convert?
WindDrop
12.05.2015 21:06+1Вот раз — http://habrahabr.ru/post/94435/, вот два — http://habrahabr.ru/post/245165/.
И забудьте про такие скрипты.
newkamikaze
13.05.2015 21:31Понятное дело, такие скрипты часто нужны, другой вопрос, что городить свой огород имеет смысл лишь в случае очень специфических требований, когда ни одно из готовых решений не устраивает. Здесь, как я вижу, таких требований нет. В моей практике чаще нужно генерировать какое-то изображение: бэйджик даты, превьюшку галереи с эффектом сепии и рандомной рамкой, диплом с именем. Но и в этом случае иногда есть универсальные решения. Например у нас используется скрипт на phantom.js для получения скриншота с сайта (или отдельной его части).
iSage
oh. god. why.