Создание миниатюр разных размеров с суффиксом имени файла

Создание миниатюр разных размеров с суффиксом имени файла

Я боролся с этой проблемой несколько дней и не смог найти решение ни на 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»

Связанный контент