Я боролся с этой проблемой несколько дней и не смог найти решение ни на StackExchange, ни в Google.
У меня следующая структура каталогов (упрощенная, потому что на самом деле файлов более 20 000). Каждый год имеет каталог для каждого месяца. Каждый месяц имеет каталог для каждого дня.
2017
01
01
...
31
...
12
2018
2019
В этих каталогах находятся изображения:
123_43e6d929fcdbfa00aee1892893127b34.png
4567_24a847285bae9ddb6d3c33d237c6d481.jpg
Чего я хочу достичь
Я хотел бы создать миниатюрные версии (измененные и обрезанные) каждого файла, сохраненного в том же месте назначения, но с суффиксами. Таким образом, приведенный выше пример будет:
123_43e6d929fcdbfa00aee1892893127b34.png
123_43e6d929fcdbfa00aee1892893127b34-120x160.png
123_43e6d929fcdbfa00aee1892893127b34-300x300.png
123_43e6d929fcdbfa00aee1892893127b34-800x800.png
4567_24a847285bae9ddb6d3c33d237c6d481.jpg
4567_24a847285bae9ddb6d3c33d237c6d481-120x160.jpg
4567_24a847285bae9ddb6d3c33d237c6d481-300x300.jpg
4567_24a847285bae9ddb6d3c33d237c6d481-800x800.jpg
Что я пробовал
for file in *.jpg; do convert $file -set filename:base "%[base]" -resize 120x160^ -gravity center -extent 120x160 -quality 90\> "%[filename:base]-120x160.jpg"; done
Я думаю, это вызывает небольшой цикл, потому что мы создаем новые изображения по мере прохождения цикла. Поэтому я создал PHP-скрипт, чтобы добавить проверку, и придумал это:
foreach ($all_files as $file) {
$path = $file->getPathname();
$resized = str_replace_last('.', '-120x160.', $path);
if (preg_match('/\-\d{3}x\d{3}\./', $path) || file_exists($resized) || substr($path,-4) != '.jpg') continue;
exec("convert \"$path\" -set filename:base \"%d/%[base]-120x160.jpg\" -resize 120x160^ -gravity center -extent 120x160 -quality 95\> \"%[filename:base]\";
convert \"$path\" -set filename:base \"%d/%[base]-300x300.jpg\" -resize 300x300^ -gravity center -extent 300x300 -quality 95\> \"%[filename:base]\";
convert \"$path\" -set filename:base \"%d/%[base]-800x800.jpg\" -resize 800x800^ -gravity center -extent 800x800 -quality 95\> \"%[filename:base]\"");
}
Это отлично работает, однако, это займет целую вечность, чтобы создать все 66000 миниатюр, а также убить производительность сервера. Я думал, что, возможно, это mogrify
будет ответом, но это должно быть запущено в каждом каталоге, а каталогов тысячи.
Может ли кто-нибудь предложить решение - shell, bash, php, что-нибудь, что может этого добиться?
Спасибо
решение1
Я думаю, это приводит к некоторому зацикливанию, поскольку мы создаем новые изображения по мере прохождения цикла.
Нет, список файлов создается, затем итерируется, поэтому вам не придется повторно обрабатывать созданные вами файлы. Если вы хотите избежать повторной обработки файла при повторном запуске скрипта, то это другой вопрос:
- Вы можете избежать обработки больших пальцев, исключив их с помощьюотрицательное совпадение(или более строгое положительное совпадение),
- Но вам, возможно, следует также иметь некоторую логику, чтобы избежать обработки файла, если большие пальцы уже там (я предполагаю, что исходное изображение никогда не обновляется)
... но насколько я могу судить, именно это и делает ваш PHP-код.
Это отлично работает, однако создание всех 66 000 миниатюр займет целую вечность, а также снизит производительность сервера.
Создание трех больших файлов из 66K действительно займет некоторое время. Но что бы вы ни делали, вам придется выполнить масштабирование и перекодирование для каждого большого файла, а это то, что займет некоторое время CPU. Вы могли бы представить себе, как избежать чтения исходного файла три раза, но кэширование файлов в Linux позаботится об этом.
Если вы не хотите, чтобы этот процесс перегружал ваш сервер, вы можете добавить команду сна на 1-2 секунды после каждого образа (или каждого ежедневного каталога).
решение2
Вот пример использования Make. Преимущество здесь в том, что он не будет пытаться сделать что-либо из того, что у вас уже есть. Поэтому можно безопасно запустить его на некоторое время, а затем остановить, а затем запустить снова, когда у вас будет время. Вы также можете указать флаг -j, например, "make -j 4" будет использовать до 4 ядер ЦП одновременно (что будет быстрее, но потребует больше ресурсов).
ifeq (${MAKELEVEL}, 0)
# path to this Makefile
mfile = Makefile
endif
subdirs = $(shell find * -type d -print -prune)
files = $(wildcard *.jpg) $(wildcard *.png)
thumbs = $(wildcard *-120x160.*) $(wildcard *-300x300.*) $(wildcard *-800x800.*)
notthumbs = ${basename $(filter-out $(thumbs), $(files) ) }
neededthumbs = $(foreach f, ${notthumbs}, ${f}-120x160.jpg ${f}-300x300.jpg ${f}-800x800.jpg)
all: thumbs recurse
thumbs: ${neededthumbs}
recurse:
$(foreach f, ${subdirs}, $(MAKE) -C ${f} -f ../${mfile} mfile=../${mfile} all ; )
%-120x160.jpg: %.jpg
echo convert $< -resize 120x160 $@
%-300x300.jpg: %.jpg
echo convert $< -resize 300x300 $@
%-800x800.jpg: %.jpg
echo convert $< -resize 800x800 $@
%-120x160.jpg: %.png
echo convert $< -resize 120x160 $@
%-300x300.jpg: %.png
echo convert $< -resize 300x300 $@
%-800x800.jpg: %.png
echo convert $< -resize 800x800 $@
Скопируйте код в файл (обратите внимание, что отступы ДОЛЖНЫ быть ТАБУЛЯЦИЯМИ! Я знаю, верно?) под названием «Makefile» в корне вашего дерева. Перейдите в этот каталог и введите «make» (или «make -j 4» и т. д.) Чтобы замедлить работу, вы можете использовать «nice», например «nice make»