Вводные данные:
#PRODUCT_CODE# - символьный код товара. Он уникальный в рамках каталога товаров
Сайт развернут на типовом битриксовом веб-окружении BitrixVM актуальной версии.
Первая же проблема, с которой столкнулся - несуществующие пути к изображениям обрубаются на уровне nginx и на уровне php, понятное дело, нет возможности их обработать! Системный администратор сервера, на котором развернут сайт, долгое время искал пути решения данной задачи и пришел к очень простому выводу:
1. В файле /etc/nginx/bx/conf/bitrix_general.conf находим location вида:
location ~* \.(css|js|gif|png|jpg|jpeg|ico|ogg|ttf|woff|eot|otf|svg|woff2)$ {
error_page 404 /404.html;
expires 30d;
}
2. Для изображений JPG выносим обработку отдельно и отключаем 404-ю ошибку:
location ~* \.(css|js|gif|png|ico|ogg|ttf|woff|eot|otf|svg|woff2)$ {
error_page 404 /404.html;
expires 30d;
}
location ~* \.(jpg|jpeg)$ {
error_page 404 =200 /imgget.php?request_uri=$request_uri;
expires 30d;
}
В результате, в php коде используя $_GET["request_uri"] получаем путь к изображению, с которым уже можно дальше работать.
Далее, в файле-обработчике imgget.php делаем саму обработку получения изображений:
define("STOP_STATISTICS", true);
define('NO_AGENT_CHECK', true);
use Bitrix\Main\Application;
use \Bitrix\Main\Data\Cache;
require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
function GetImagesByCode($code){
$IBLOCK_ID = 2;
$cache = Cache::createInstance();
$cache_id=md5(serialize(['function'=>__FUNCTION__,'CODE'=>$code,'IBLOCK_ID'=>$IBLOCK_ID]));
$cache_time = 60*60*24*7;
$cache_dir = 'static_prod_images';
if(isset($_GET['clear_cache'])){
$cache_time = 0;
$cache->clean($cache_id,$cache_dir);
}
$result = [];
if($cache->initCache($cache_time,$cache_id,$cache_dir)){
$result = $cache->getVars();
} elseif ($cache->startDataCache() && \Bitrix\Main\Loader::includeModule('iblock')){
$arOrder = array("SORT" => "ASC");
$arFilter = array('IBLOCK_ID'=>$IBLOCK_ID,'ACTIVE'=>'Y','CODE'=>$code);
$arSelectFields = array("ID", "ACTIVE", "NAME",'PREVIEW_PICTURE','DETAIL_PICTURE','IBLOCK_ID');
$rsElements = \CIBlockElement::GetList($arOrder, $arFilter, FALSE, FALSE, $arSelectFields);
if ($arElement = $rsElements->GetNext())
{
$arMorePhotos = [];
$rs = CIBlockElement::GetProperty($arElement['IBLOCK_ID'],$arElement['ID'],"sort","asc",
['CODE'=>'MORE_PHOTO']);
while ($ob = $rs->GetNext()){
$arMorePhotos[] = [
'ID'=>intval($ob['VALUE']),
'SRC'=>\CFile::GetPath(intval($ob['VALUE'])),
];
}
$result = [
'PREVIEW_PICTURE'=>(intval($arElement['PREVIEW_PICTURE'])>0)
? [
'ID'=>intval($arElement['PREVIEW_PICTURE']),
'SRC'=>\CFile::GetPath(intval($arElement['PREVIEW_PICTURE']))
]
: false,
'DETAIL_PICTURE'=>(intval($arElement['DETAIL_PICTURE'])>0)
? [
'ID'=>intval($arElement['DETAIL_PICTURE']),
'SRC'=>\CFile::GetPath(intval($arElement['DETAIL_PICTURE']))
]
: false,
'MORE_PHOTO'=>(!empty($arMorePhotos)>0) ? $arMorePhotos : false,
];
}
$cache->endDataCache($result);
}
return $result;
}
function GetImagesSrcSet($image)
{
$sizes = [
'0' => [100, 100, 50],
'1920' => [300, 400, 80],
'960' => [200, 267, 80],
'480' => [200, 267, 80],
];
$result = [];
foreach ($sizes as $key => $arSize)
{
if (isset($image['ID']))
{
$renderImg = \CFile::ResizeImageGet(
$image['ID'],
array("width" => $arSize[0], "height" => $arSize[1]),
BX_RESIZE_IMAGE_PROPORTIONAL,
false,
false,
false,
$arSize[2]
);
if (!isset($renderImg['src']) || strlen($renderImg['src']) <= 0)
{
if (isset($image['SRC']))
{
$renderImg = $image['SRC'];
} else
{
$renderImg = \CFile::GetPath($image['ID']);
}
} else
{
$renderImg = $renderImg['src'];
}
$result[$key] = $renderImg;
}
}
$result['set'] = implode(', ', [
$result['1920'] . ' 1920w',
$result['960'] . ' 960w',
$result['480'] . ' 480w',
]);
return $result;
}
$requestedDir = current(explode('?',$_REQUEST['request_uri']));
$mode = 'full';
$arCodes = false;
$Scale = false;
if(preg_match('|-x([\d]+)\.jpg$|',$requestedDir,$matches)){
// Если в шаблоне зашито масштабирование фото
$Scale = $matches[1];
$requestedDir = preg_replace('|-x([\d]+)\.jpg$|','.jpg',$requestedDir);
}
if(preg_match('|-preview\.jpg$|',$requestedDir)){
// если это ссылка на превью фото
$mode = 'preview';
$requestedDir = str_replace('-preview','',$requestedDir);
} elseif (preg_match('|-([\d]+)\.jpg$|',$requestedDir,$matches)){
// если указан порядковый номер
$arCodes = [
[
'num'=>false,
'code'=>current(explode('.jpg',substr($requestedDir,1))),
'mode'=>'full'
],
[
'num'=>$matches[1],
'code'=>preg_replace('|-([\d]+)\.jpg$|','',substr($requestedDir,1)),
'mode'=>'additional'
]
];
}
$src = false;
$photo_id = false;
if(empty($arCodes)){
$code = current(explode('.jpg',substr($requestedDir,1)));
$CodePhotos = GetImagesByCode($code);
if(!empty($CodePhotos)){
if($mode=='full'){
if($CodePhotos['DETAIL_PICTURE']){
$src = $CodePhotos['DETAIL_PICTURE']['SRC'];
$photo_id = $CodePhotos['DETAIL_PICTURE']['ID'];
} elseif($CodePhotos['PREVIEW_PICTURE']){
$src = $CodePhotos['PREVIEW_PICTURE']['SRC'];
$photo_id = $CodePhotos['PREVIEW_PICTURE']['ID'];
} elseif (!empty($CodePhotos['MORE_PHOTO'])){
$src = $CodePhotos['MORE_PHOTO'][0]['SRC'];
$photo_id = $CodePhotos['MORE_PHOTO'][0]['ID'];
} else {
$src = SITE_TEMPLATE_PATH.'/img/no_photo.jpg';
}
} elseif($mode == 'preview') {
if($CodePhotos['PREVIEW_PICTURE']){
$src = $CodePhotos['PREVIEW_PICTURE']['SRC'];
$photo_id = $CodePhotos['PREVIEW_PICTURE']['ID'];
} elseif($CodePhotos['DETAIL_PICTURE']){
// TODO: реализовать ресайз изображения
$src = $CodePhotos['DETAIL_PICTURE']['SRC'];
$photo_id = $CodePhotos['DETAIL_PICTURE']['ID'];
} elseif (!empty($CodePhotos['MORE_PHOTO'])){
$src = $CodePhotos['MORE_PHOTO'][0]['SRC'];
$photo_id = $CodePhotos['MORE_PHOTO'][0]['ID'];
} else {
$src = SITE_TEMPLATE_PATH.'/img/no_photo.jpg';
}
}
}
} else {
foreach ($arCodes as $arCode){
// может быть порядковый номер, а может быть часть символьного кода товара
$mode = $arCode['mode'];
$CodePhotos = GetImagesByCode($arCode['code']);
if(!empty($CodePhotos)){
if($mode=='full'){
if($CodePhotos['DETAIL_PICTURE']){
$src = $CodePhotos['DETAIL_PICTURE']['SRC'];
$photo_id = $CodePhotos['DETAIL_PICTURE']['ID'];
} elseif($CodePhotos['PREVIEW_PICTURE']){
$src = $CodePhotos['PREVIEW_PICTURE']['SRC'];
$photo_id = $CodePhotos['PREVIEW_PICTURE']['ID'];
} elseif (!empty($CodePhotos['MORE_PHOTO'])){
$src = $CodePhotos['MORE_PHOTO'][0]['SRC'];
$photo_id = $CodePhotos['MORE_PHOTO'][0]['ID'];
} else {
$src = SITE_TEMPLATE_PATH.'/img/no_photo.jpg';
}
} elseif($mode == 'additional') {
if (!empty($CodePhotos['MORE_PHOTO'])
&& !empty($CodePhotos['MORE_PHOTO'][(intval($arCode['num'])-1)])){
$src = $CodePhotos['MORE_PHOTO'][(intval($arCode['num'])-1)]['SRC'];
$photo_id = $CodePhotos['MORE_PHOTO'][(intval($arCode['num'])-1)]['ID'];
}
}
}
}
}
if($src){
if($Scale){
$srcSet = GetImagesSrcSet(['ID'=>$photo_id,'SRC'=>$src]);
if(isset($srcSet[$Scale])){
$src = $srcSet[$Scale];
} else {
// тут делаю сжатие в квадрат, но можно внести правки в шаблон и вытягивать нужные размеры ...
$renderImg = \CFile::ResizeImageGet(
$photo_id,
array("width" => $Scale, "height" => $Scale),
BX_RESIZE_IMAGE_PROPORTIONAL,
false,
false,
false
);
if (!isset($renderImg['src']) || strlen($renderImg['src']) <= 0)
{
$renderImg = $src;
} else
{
$renderImg = $renderImg['src'];
}
$src = $renderImg;
}
}
header("Content-type: image/jpeg");
echo file_get_contents(Application::getDocumentRoot().$src);
exit();
} else {
Bitrix\Iblock\Component\Tools::process404(
'',
true,
true,
true,
false
);
}
Вот таким нехитрым способом получили пути к изображениям в нужном нам формате!
