ファイル名のサフィックスを使用してさまざまなサイズのサムネイルを作成する

ファイル名のサフィックスを使用してさまざまなサイズのサムネイルを作成する

私は数日間この問題と格闘してきましたが、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]\"");
}

これはうまく機能しますが、66,000 個のサムネイルすべてを作成するにはとてつもなく時間がかかり、サーバーのパフォーマンスも低下します。これがmogrify解決策になるかもしれないと思いましたが、各ディレクトリで実行する必要があり、ディレクトリは数千あります。

誰か解決策を提案していただけませんか?シェル、bash、php など、これを実現できるものなら何でもいいです。

ありがとう

答え1

ループしながら新しい画像を作成するため、少しループが発生すると思います。

いいえ、ファイルのリストが作成され、反復処理されるため、作成したファイルを再処理することはありません。スクリプトを再実行するときにファイルの再処理を避けたい場合は、別の問題になります。

  • 親指を除外することで、親指の処理を避けることができます。否定一致(またはより厳密な正の一致)、
  • しかし、サムネイルがすでに存在する場合は、ファイルの処理を回避するためのロジックも必要かもしれません(ソース画像は決して更新されないと想定しています)。

...しかし、私が知る限り、これが PHP コードが行うことです。

これはうまく機能しますが、66,000 個のサムネイルすべてを作成するには非常に時間がかかり、サーバーのパフォーマンスも低下します。

66K ファイルから 3 つのサムネイルを生成するには、確かに時間がかかります。しかし、何をするにしても、各サムネイルのスケーリングと再エンコードを実行する必要があり、これが CPU を消費することになります。ソース ファイルを 3 回読み取らないようにすることも考えられますが、Linux のファイル キャッシュがそれを処理します。

プロセスがサーバーを占有しないようにするには、各イメージ (または各日のディレクトリ) の後に 1 ~ 2 秒のスリープ コマンドを追加できます。

答え2

以下は Make を使用した例です。ここでの利点は、すでに存在するものを作成しようとしないことです。そのため、しばらく実行してから停止し、後で時間があるときに再度実行しても安全です。-j フラグを指定することもできます。たとえば、「make -j 4」は、一度に最大 4 つの CPU コアを使用します (高速になりますが、より多くのリソースを使用します)。

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」というファイルにコピーします (インデントは必ずタブにする必要があります。わかりますよね?)。そのディレクトリに cd して、「make」(または「make -j 4」など) と入力します。実行速度を遅くするには、「nice make」のように「nice」を使用します。

関連情報