Pull to refresh

Comments 29

Решал подобную задачу на Apache иначе:
Все защищенные изображения хранил в недоступной через веб папке. Через простенький .htaccess настроил проверку: если пришел запрос на изображение и его нет в папке кэша (с водяным знаком, правильным размером...) — создаем его на лету и отдаем, иначе апачем же прозрачно редиректим на имеющееся защищенное изображение и отдаем ничего не подозревающему браузеру.
Поделитесь .htaccess этот вариант гораздо интереснее.
Так как через месяц, при появлении новых картинок, пришлось запускать скрипт заново, а это уже излишки работы.
Если защищенное изображение создано — отдаем апачем, если нет, создаём и отдаем через PHP.
RewriteCond %{REQUEST_URI} ^/images(.*)\.(jpe?g|png)$
RewriteCond %{DOCUMENT_ROOT}/web%{REQUEST_URI} !-f
RewriteRule (.*) /web/image.php/$1 [L]
RewriteCond %{DOCUMENT_ROOT}/web%{REQUEST_URI} -f
RewriteRule (.*) /web/$1 [L]
Оригиналы картинок в /images/..., доступные пользователям в /web/images/… Скрипт наложения лого и ресайза /web/image.php. Логика защиты: если файл больше минимального размера, добавляем водяной знак, записываем и отдаем. Логика ресайза: если файла/папки /images/95x30/image.jpg не существует, создаем из /images/image.jpg превьюху размером 95х30, записываем в соответствующую папку и отдаём (можно без папки, но мне больше нравится сохранять неизменным имя файла).

Наши предложения в комментариях:


$new_dir = null;
$dir_files = opendir($dir);
while(false !== ($file = readdir($dir_files)))
{

    if($file != '.' && $file != '..')
        $new_dir[] = $dir."/".$file;
}

->


$new_dir[] = glob("$dir/*");

Никогда так не делайте, ни на коленке, ни на станке с программным управлением:


$new_dir = null;

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


Заодно пропадет необходимость в лишнем условии if($new_dir)


Ну и разделить эту кучу-малу не помешало бы.
одна функция собирает в массив абсолютные пути ко всем файлам в дереве, а вторая накладывает водяной знак. И в аккуратном цикле вызывает вторую для результатов первой.

Я обязательно воспользуюсь всеми советами, и усовершенствую данную версию.
Если есть ещё, прямо кусочки кода которые стоит заменить, сообщите.
Я не претендую на идеальный продукт, но нужно учиться и стремиться к совершенству.
Добавлю еще
$new = str_replace("img","img2",$check); //Заменяем имя папки

У вас очевидно ни когда не было под папок с именами img* (например img123), так можно изображения потерять, так как они будут по пути img2*

Большое спасибо, это мы записали, но ещё не пофиксили. Как выход, это прописывать в ручную исходную и конечную папку. И как уже предлагали, разбивать все на функции для удобства.

UFO just landed and posted this here
Чуть по подробнее, не понял сразу
Жованный крот. Зачем накладывать это в php? Обойти картинки, дернуть размер, потом вызвать
compose over -quality 100 -geometry +x+y watermark_file src_file dst_file (где x & y позиция куда втыкать ватермарк)
будет в 100 раз быстрее.

Да и opendir/readdir тоже сомнительно.
$scanned_directory = array_diff(scandir($directory), array('..', '.'));
Решает все чтение папки одной строкой
А как вставить в вашу команду, вычисления размера куда вставлять ватермарк и изменении размера ватермарка в зависимости от картинки. В итоге, скорее всего, придется писать скрипт. А тут уж кто на что горазд.
За $scanned_directory = array_diff(scandir($directory), array('..', '.')); большое спасибо, в этом направлении я не думал…
Я привел пример команды для утилиты composite из пакета ImageMagick, бинарной консольной утилиты, которая выполнит наложение водяного знака в десять раз быстрее «загрузки» обоих картинок в php с последующим сращиванием.

Кусок кода для расчета позиции знака ровно посередине
    	// размеры картинки и водяного знака 
    	// ( если размеры знака известны заранее, можно и не получать их вообще)
    	$imagesize    = getimagesize($sourceFile);
    	$wmsize 	    = getimagesize($watermarkfile);
    	// размеры картинки
    	$width 	= $imagesize[0];
    	$height     = $imagesize[1];
    	// размеры знака	
    	$w		= $wmsize[0];
    	$h		= $wmsize[1];
    	//центральная позиция
    	$x 		= $width/2 - $w/2;
	$y 		= $height/2 - $h/2;

	$command = '/usr/local/bin/composite -compose over -quality 93  -geometry +'.$x.'+'.$y.' '.$watermarkfile.' "'.$sourceFile.'" "'.$destinationFile.'"';
	exec ($command,$out,$exitcode);
	// проверка $exitcode уже кому как нравится


Скрипт обхода писать конечно придется. И целевые папки, если вам нужна копия тоже создавать придется. И, для качества, лучше не ресайзить марку, а накладывать разные готовые марки в зависимости от размера картинки.
А вообще в общем и целом — марки отлично накладываются при желании на лету в том же nginx. И хранить придется только оригиналы картинок
А вообще в общем и целом — марки отлично накладываются при желании на лету в том же nginx. И хранить придется только оригиналы картинок

Вопрос нагрузки, у клиента на странице открывается сразу 40 фотографий. И если все 40 будут на лету обрабатываться и ещё и клиентов будет 100 как бы наш дешевый сервак не упал.
Интересное решение у Eureka выше, это при запросе создавать файл и уже потом его хранить. Так работает движок OpenCart с кэшированием картинок.
Кусок кода для расчета позиции знака ровно посередине

Это просто шикарно, обязательно протестирую!
Вопрос нагрузки, у клиента на странице открывается сразу 40 фотографий. И если все 40 будут на лету обрабатываться и ещё и клиентов будет 100 как бы наш дешевый сервак не упал.

В nginx + resize умеет обработанное кешировать. На столько, на сколько скажете. Хотя обычно это применяется только для ресайза, генерации более мелких вариантов оригинальной картинки.
Спасибо, для меня это было открытием. Пойду более подробно читать об nginx.
Почитайте про RecursiveDirectoryIterator и SPL в общем
Для обхода директории, есть множество способов.
В следующем проекте, обязательно воспользуюсь этим.
А вот про SPL первый раз слышу… Я правильно понял что это: php.net/manual/ru/book.spl.php и как его можно применить?
Да, правильно поняли. Прочитайте введение.
Собственно конечно можно и так, но почему религия мешает сделать копию каталога на сайте, а затем пройти её рекурсивно с обработкой IM?
cp -R img_cat1 img_cat2
for i in `ls *.jpg -R`
do
echo $i
composite -resize 800 -watermark 10.0 watermark.png $i $i
done

К чему танцы с бубном в гамаке?
У вас решение, для которого нужно обладать некоторыми знаниями.
Мы же старались создать решение для обычного или начинающего пользователя.
И как уже писали, скорее всего, в этом коде вы не учитываете что картинки будут разного размера, и watermark нужно менять.
Ну вопрос конкретного применения ImageMagick я не учитывал, понятно что скрипт может варьироваться, но это не отменяет того, что встроенные инструменты ОС для файловых операций предпочтительней.
Исходя из php что-то вроде:
exec('cp -R img_cat1 img_cat2'); 

Будет сильно предпочтительней этого:
$fileName = basename($check); //Определяем имя файла
$new = str_replace("img","img2",$check); //Заменяем имя папки
$put = substr($new,0,-strlen($fileName)); //Определяем путь до папки
if (!file_exists($put)) {
    mkdir($put, 0777, true); //Если папки нет, то создаем
}

Совет: никогда не нужно прикрываться воображаемым "начинащим пользователем", ради которого все ваши старания :)
Во-первых, это никак не может объяснить плохой код.
Во-вторых, начинающему тоже надо когда-то начинать расти, и получать эти самые "специальные знания".

Приходилось такое несколько раз решать. Думаю, что лучше было бы реализовать динамическое добавление водяных знаков. Выше это уже предлагалось, но я вставлю свои несколько копеек. Такое решение позволит избежать некоторых проблем:

1) При добавлении новых изображений не придется прогонять ваш скрипт по новой, заставлять контент-менеджеров лепить на изображения водяные знаки (они сделают это криво) или писать отдельный скрипт, который делает это автоматически при загрузке каждого нового изображения.

2) Вы предлагаете, если я правильно понимаю, заменить старые изображения без водяных знаков на новые с ними. А старые, «пустые», забэкапить. И после не загружать на сервер изображения без водяного знака (как это у вас будет реализовано — не совсем понятно). А теперь представьте, что сменились требования к размещению или виду водяного знака. В результате вы получите бэкап с некоторой частью изображений без знаков + (возможно) некоторое количество изображений для которых нет копии без знака. И это станет куда большей головной болью, чем ваша изначальная задача.

И, кстати, бэкапы с «какая-то часть старых данных» теряются обычно в первую очередь.

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

Оригиналы Изображения хранятся в папке, и работа ведётся с ними, при добавлении новых картинок, стоит прогнать скрипт заново и перегегирировать все картинки.
При изменении требований к водяному знаку, так же можно внести коррективы в скрипт и прогнать. У нас на проекте в 10к картинок это занимает минуты 2.


Скрипт, выполняет задачу: добавить ко всем картинкам водяной знак, он ее выполняет. И требует минимальных временных затрат.


Да почитав комментарии, есть желание его улучшить, ускорить, добавить функций и вариантов.


Большое спасибо, за развитие идеи.

Думаю, вы все-таки выбрали самый неоптимальный путь. Я с трудом могу представить проект, где ваш вариант будет оптимальным. Разве что вы экономите какие-то крохи на разнице в выдаче изображений напрямую и обработки «по запросу».

Идея прогонять скрипт каждый раз, да еще и по полной (если я все правильно понял) при появлении каждого нового изображения… Это может быть приемлемым решением, если у вас раз в полгода одно изображение добавляется. Но откуда тогда 10K изображений? Это ведет к:

  1. Появлению на некоторый срок на сайте изображений без водяных знаков: пока скрипт не доработает или если его забыли запустить
  2. Увеличению сложности обслуживания. Контент-менеджеры (мой личный опыт) вечно забывают делать дополнительные действия, если они не совсем очевидны.
  3. Уменьшение надежности всей системы. Ой-ей, храните бэкапы ваших «чистых» картинок. При таких массовых перезаписях однажды может случиться неприятный сюрприз
  4. Создание периодических пиков нагрузки в момент исполнения вашего скрипта
  5. Необходимость хранить два полных комплекта изображений одновременно: комплект с и комплект без водяных знаков.
  6. С ростом количества изображений все неприятные последствия выбранного вами решения будут расти: будет увеличиваться нагрузка на сервер, время обработки и т.д.


Поэтому я бы рекомендовал применить такой механизм (в общем виде, конкретная реализация зависит от того, какой у вас там стек):

  • Создаем каталог для хранения кэша изображений. Т.е. изображений с водяными знаками
  • При запросе изображения на выдачу выясняем есть ли уже готовое изображение в «кэше»
  • Если есть — отдаем его. Если нет — обрабатываем изображение, сохраняем результат в кэш и отдаем пользователю
  • Периодически чистим кэш. Самый простой вариант — удаляем раз в X минут/часов все файлы старше некоторого N. Но в идеале, наверное, стоит учитывать все-таки не время создания файла, а время последнего его использования. Не запрашивалось ни разу за последние X часов? Удаляем из кэша. Тут надо быть аккуратным, чтобы не сделать какую-нибудь ресурсозатратную жуть в качестве системы учета времени последнего использования.


Думаю, такой вариант решения задачи будет лучше выбранного вами по практически всем параметрам.

Да и это был первый комментарий, и ко второй версии мы это все добавим.

Это все делаеться в nginx — зачем городить в скрипте? Лишняя работа по обновлению, как было написано раньше.

Sign up to leave a comment.

Articles

Change theme settings