HAROLABO Tech Blog

テクノロジーを使いやすく!ハロラボの技術情報発信メディアです。

Cloudflare Imagesでプライベートな画像を管理する

書き散らしメモアプリProjmoで、これまで画像の保存にFirebase Storageを使っていたのですが、コストを削減するためにCloudflare Imagesに変更しました。

書き散らしメモアプリProjmoを開発しています。

projmo.com

Projmoでは、画像を添付する機能があるのですが、画像の保存先は悩みの種でした。 インフラとしてはFirebaseを使っているので、Firebase Storageを使うのが権限の制御的にも無難なため、初期にはFirebase Storageで実装しました。

しかし、Firebase Storageの権限制御はそんなに柔軟には使えず、キャッシュ制御も難しいということがあって、いろいろ試したのですが、結局代替サービスを考えることにしました。

そこで白羽の矢を立てたのがCloudflare Imagesです。

コスト

Cloudflareは送信料金が無料というのが特徴です。 エグレス料金というものですね。 つまり、どれだけのサイズをダウンロードしても、料金がかからないということです。

ただし、回数による課金はかかります。 つまり、回数は少ないけどサイズは大きいというコンテンツを扱う場合に適しているといえると思います。

2023年11月現在、Basicプランの場合で料金は以下の通りとなっています。 10万個の画像を$5で保持できるのはとても魅力的ですね。

  • $5 per month per 100k images stored
  • $1 per month per 100k images delivered

ちなみに、Cloud Storageの価格は以下の通りです。 実際に料金シミュレーターにかけると、Projmoの場合、保存の方がコストがかかるので、 Cloudflareを選んだ動機は送信料金のためというより保存料金のためということになります。

  • 保存 $0.026/GB
  • ダウンロード $0.12/GB

アップロード

Firebase Storageの場合Firebase Authenticatioinと連動するルールで制御していたアップロードの権限ですが、外部サービスの場合は簡単ではありません。 自サーバーを経由するのも通信量の観点でよろしくありません。

Cloudflare Imagesには、Direct Creator Uploadという仕組みがあります。 秘密キーをクライアントに渡さないで画像をアップロードするために、サーバーで1回限りのアップロードURLを生成できるという仕組みです。 これを使うことで、自サーバーで権限制御しながらURLを生成し、クライアントでアップロードする、といったことが可能になります。

REST APIで使えますのでとても簡単に使えます。

developers.cloudflare.com

ダウンロード

画像のダウンロードの制御は、期限付きの署名付きURLを使う方法があります。 Cloudflare Imagesにもその機能がありますが、利用方法がやや特殊です。

署名付きURLを取得するAPIはなく、既存のダウンロードURLを自前で署名する、というやり方になっています。

  1. 画像IDからダウンロードURLを作る
  2. 有効期限をURLに付加する
  3. ハッシュ値を生成する
  4. ハッシュ値をダウンロードURLに付加する

以上の手順を踏んで生成したURLが、署名付きURLになります。 このアルゴリズムは以下のページでJavascriptのコードで示されているだけなので、例えばPythonなどで実現しようとすると、アルゴリズムを解読して実装する必要があります。 APIが提供されていないのは、Workerで使うことを想定しているからでしょう。

developers.cloudflare.com

ちなみにPythonで書くと、以下のようになります。

def get_signedUrl(id):
    # The base URL of the image
    base_url = f"YOUR_DOWNLOAD_URL/{id}/public"
    parsed_url = urlparse(base_url)
    url_path = parsed_url.path

    # Expiration time
    expiration_time = int(time.time()) + 3600

    # Append the expiration time to the URL
    token_data = f"{url_path}?exp={expiration_time}"

    # Create the signature
    sig = hmac.new(bytes(sig_key, 'utf-8'), 
                    msg=bytes(token_data, 'utf-8'), 
                    digestmod=hashlib.sha256).hexdigest()

    # Append the token to the URL
    return f"{base_url}?exp={expiration_time}&sig={sig}"

リサイズ・圧縮

その他にもURLによって画像サイズを変える、圧縮して配信するなどの機能があります。 主にダウンロード速度を改善するための機能であるといえそうです。

終わりに

Cloudflare Imagesを使うことで、コストにおびえながら画像を保存、表示しなくてもよくなりそうです。 まだ使い始めたばかりなので、どのくらいの効果があるかは分かりませんが、期待を込めてウォッチングしようと思います。