Cloudinary の冗長な条件分岐をユーザ定義変数ですっきりさせてみた

2023.11.21

先日、Cloudinary のユーザ定義変数という機能についてご紹介しました。

ここでは、事前に設定内容を登録する Named Transformation と組み合わせ、配信 URL 内で変数を定義することで柔軟な画像変換ができるメリットについて説明しました。

今回は、冗長となってしまいがちな条件分岐の変換パラメータ if 構文の中で活用する方法をご紹介します。

おさらい:条件分岐変換とは?

if を使うと条件によって異なる処理をさせることができます。詳しくはこちらのブログにて。次のような使用例も紹介しています。

  • 元画像が一定サイズより小さければ、異なる変換サイズを適用
  • 元画像に顔が含まれていたら、異なる変換サイズを適用
  • 画像アセットに付与されているタグに応じて、異なる色のウォーターマークを適用

if パラメータは、以下のような構文パターンがあります。

  • /if_w_gt_500,w_300,e_red:50
  • /if_w_gt_500/w_300,e_red:50/e_blur/if_else/w_300/if_end
  • /if_w_gt_500/w_300,e_red:50/e_blur/if_end

おさらい:ユーザ定義変数とは?

ユーザ定義変数を使うと、URL内で任意の変数を定義して変換パラメータの一部に活用できます。宣言するときは「$ + 任意の変数名 + _ + 代入する値」、呼び出すときは「$ + 変数名」と表記します。

  • 例:/$imgwidth_270/w_$imgwidth

前回のブログでは、変数の呼び出し部分を Named Transformation に事前設定し、実際の変数の値を URL 内で宣言する方法をユースケースとして紹介しました。

組み合わせ活用

以前の条件分岐変換のブログで紹介したウォーターマークの例では、複数の if 条件の中で冗長な部分があります。
今回は、これにユーザ定義変数を組み合わせることでスッキリさせてみましょう。

元の複雑な条件分岐

まずはこちらが元の変換パラメータです。

https://res.cloudinary.com/CLOUD_NAME/image/upload
  /w_330
  # ↓ wm0 タグがあれば、何もしない(何かは指定する必要があるため、a_0 や o_100 を指定する)
  /if_!wm0!_in_tags,o_100
  # ↓ wm1 タグがあり、wm0 タグがなければ、黒文字のウォーターマーク
  /if_!wm1!_in_tags_and_!wm0!_nin_tags,a_20,co_black,fl_relative,l_text:verdana_30_bold:classmethod,o_40,w_0.6
  # ↓ wm2 タグがあり、wm0 タグも wm1 タグもなければ、白文字のウォーターマーク
  /if_!wm2!_in_tags_and_!wm0!_nin_tags_and_!wm1!_nin_tags,a_20,co_white,fl_relative,l_text:verdana_30_bold:classmethod,o_40,w_0.6
  # ↓ wm0 タグも wm1 タグも wm2 タグもなければ、黄文字のウォーターマーク
  /if_!wm0!_nin_tags_and_!wm1!_nin_tags_and_!wm2!_nin_tags,a_20,co_yellow,fl_relative,l_text:verdana_30_bold:classmethod,o_40,w_0.6
  /v1/path/to/file.jpg

ifの部分は全部で386文字。

分解しないで平文にしたらかなり長いですね。文字色以外は同じスタイルのオーバーレイを適用するので、この部分に変数を導入してその冗長をなくしていきたいと思います。

【補足】複数文字列を指定するダブルコロン (:)

私は今回の条件でこの部分 !wm0!_nin_tags_and_!wm1!_nin_tags を久々に見返して「まずダブルコロンで短くできるんじゃ?」と思いました。
「wm0 も wm1 も両方タグにあれば」は次の2つの書き方ができるのです。

  • if_!wm0!_in_tags_and_!wm1!_in_tags
  • if_!wm0:wm1!_in_tags

しかし、nin オベレータ (=not in)で if_!wm0:wm1!_nin_tags とすると「両方なければ」ではなく「少なくともいずれかがなければ」となってしまいます。

例えば4つ目の条件を if_!wm0:wm1:wm2!_nin_tags へ書き換えると、3つのすべてがタグにある場合を除いて必ず真となります。他の条件と重複してしまい、結果としてほぼ必ず黄色のウォーターマークになるのです。

ということでこれは使えません。

要件の確認

今回の条件を一旦 Javascript コード風に整理してみました。

if ("wm0" nin tags) {
    if ("wm1" in tags) {
        var color = "black";
    } else if ("wm2" in tags) {
        var color = "white";
    } else {
        var color = "yellow";
    }
    console.log(`a_20,co_${color},fl_relative,l_text:verdana_30_bold:classmethod,o_40,w_0.6`);
}

注意しなければならないのは、残念ながら Cloudinary の変換パラメータでは現時点で if 構文に else if のような機能がありません。そのため元の変換でもそれぞれ独立したif文となっています。

また、if や else の中に入れ子で if をネストすることもできないのです。

ユーザ定義変数の導入

その①

元の変換に基づいて変数を導入しました。ウォーターマークを付与しない1行目の条件では、フェイクで仮の変数を定義しておき、5行目の if 文でその値でない場合に限りウォーターマークを適用するようにします。

~/if_!wm0!_in_tags,$color_!undefined!
  /if_!wm1!_in_tags_and_!wm0!_nin_tags,$color_!black!
  /if_!wm2!_in_tags_and_!wm0!_nin_tags_and_!wm1!_nin_tags,$color_!white!
  /if_!wm0!_nin_tags_and_!wm1!_nin_tags_and_!wm2!_nin_tags,$color_!yellow!
  /if_$color_ne_!undefined!,a_20,co_$color,fl_relative,l_text:verdana_30_bold:classmethod,o_40,w_0.6

326文字。

その②

1つ目のif文を「wm0がない場合」として最後に持ってくることで、フェイクの定義が不要になり短くなりました。

~/if_!wm1!_in_tags_and_!wm0!_nin_tags,$color_!black!
  /if_!wm2!_in_tags_and_!wm0!_nin_tags_and_!wm1!_nin_tags,$color_!white!
  /if_!wm0!_nin_tags_and_!wm1!_nin_tags_and_!wm2!_nin_tags,$color_!yellow!
  /if_!wm0!_nin_tags,a_20,co_$color,fl_relative,l_text:verdana_30_bold:classmethod,o_40,w_0.6

283文字。

その③

そもそも前半の if 文内では変数の定義しかしておらず、ここでは変換は行われないので重複して該当しても問題ありません。

優先順位に従って変数が上書きされるよう並べ替え、最後の条件でフィルタしているwm0は前3つの条件で冗長なので消してしまいます。

~/if_!wm2!_in_tags,$color_!white!
  /if_!wm1!_in_tags,$color_!black!
  /if_!wm1!_nin_tags_and_!wm2!_nin_tags,$color_!yellow!
  /if_!wm0!_nin_tags,a_20,co_$color,fl_relative,l_text:verdana_30_bold:classmethod,o_40,w_0.6

207文字。

導入の結果

今回の例ではユーザ定義変数を使うことで、最終的に386→207と半分近くにまで変換パラメータを短くすることができました!

一方で、短くして構成を変えたことで「wm0→wm1→wm2→なし」という優先順位である点は分かりづらくなったかもしれません。

短いことでパフォーマンスは上がるか?

今回短くしたことによってパフォーマンス上でメリットがあるか、つまり変換・配信がより速くなるのかを見てみました。

コールドキャッシュの状態でいくつかのアセットにブラウザ(Chrome)でアクセスし、レスポンスヘッダを確認しました。以下、それぞれのサーバレスポンス待機時間とcloudinaryのサーバタイミングです。

適用したファイル 旧パラメータ 新パラメータ
画像1 (jpg) 611ms / 214ms 245ms / 110ms
画像2 (jpg) 175ms / 41ms 423ms / 72ms
画像3 (png) 455ms / 217ms 333ms / 164ms

何度か試してもそれぞれ似たような結果となり、他のアセットで試してもまちまちでした。
各変換パラメータを Named Transformation に登録してもやはり結果は大きく変わらずアセットによってバラバラ。

Cloudinary では独自のアルゴリズムで処理が行われており、変換パラメータの指定方法は一概にベストプラクティスがあるわけではありません。
どうやらベース画像や組み合わせる変換によって変わるもので、必ずしもパラメータが短くシンプルなほど変換処理が速くなるとは限らないのですね。

まとめ

ということで、ユーザ定義変数を使って条件分岐の冗長を減らす方法をご紹介しましたが、とにかく短くするのではなく、URL長の制限や可読性のバランスを見て検討いただくのが良いでしょう。

余談ですが、
今回は正しく変換適用されるか動作確認するために wm0 のみ、wm1 と wm2 など様々な組み合わせのタグを付与した画像を計7個用意しました。
ですが、もし手っ取り早く条件分岐のロジックを検証したければ、ここでも$tags_!wm1:wm2:hoge!/if_!wm2!_in_$tags,~のように変数を使うことで、一つのアセットでURLを変えるだけでいろんなパターンが確認できますね。

以上、ぜひお試しください!


クラスメソッドはCloudinaryのパートナーとして導入のお手伝いをさせていただいています。お気軽にこちらからお問い合わせください。こちらから無料でもサインアップいただけます。