はじめに
現在、Djangoを使ったTwitterクローンを作成しています。画像ストレージにCloudinary
を使用しているのですが、アップロード画像のリサイズ方法に詰まったので、やり方をまとめておこうと思います。
前提条件
以下の環境を想定しています。
Cloudinaryの設定やTweetモデルの定義は以下の通りです。
- settings.py
MEDIA_URL = "/media/" CLOUDINARY_STORAGE = { "CLOUD_NAME": env("CLOUDINARY_NAME"), "API_KEY": env("CLOUDINARY_API_KEY"), "API_SECRET": env("CLOUDINARY_API_SECRET"), }
- models.py
class Tweet(modes.Model): """ツイート情報の格納用モデル""" class Meta: db_table = "tweet" user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) content = models.TextField("ツイート内容", null=False, blank=False) image = models.ImageField("ツイート画像", upload_to="tweets/", blank=True)
上記設定で画像アップロードすると、Cloudinary上の/media/tweets
フォルダに格納されていきます。(下記イメージ)
Cloudinaryの配信URLについて
デフォルトの形式
公式によると、Cloudinaryのデフォルトの配信URLの形式は以下のようになります。
https://res.cloudinary.com/<cloud_name>/<asset_type>/<delivery_type>/<transformations>/<version>/<public_id>.<extension>
それぞれの要素について簡単にまとめます。
<cloud_name>
:Cloudinaryのアカウント名<asset_type>
:配信するアセットタイプ(image
,video
,raw
のいずれか)<delivery_type>
:配信タイプ(デフォルトはupload
)<transformations>
:変換パラメータ、カンマ区切りで指定する(※何も指定しないとオリジナルのファイルが配信される)<public_id>
:アセットの一意な識別子(フォルダ構造が入る)
ここで注目するべきは<transformations>
の部分です。ここにトランスフォーメーション情報(リサイズやクロップ設定など)を追加することで動的に画像を変換できます。
リサイズした画像の例
例えば、<transformations>
の部分にc_fill,w_150,h_150
を付与することで、150×150pxにリサイズされた画像を配信できます。
https://res.cloudinary.com/<cloud_name>/image/upload/c_fill,w_150,h_150/v1/media/tweets/icecream_kckry7
他にも画像変換で用いるパラメータがあるため、気になる方は下記を参考にしてみてください。 dev.classmethod.jp
実際にリサイズ済みの動的なURLを作成する
Cloudinaryの配信URLの形式がわかったので、デフォルトのURLからリサイズ後のURLになるように変換する処理を作成していきます。
① 画像リサイズ用の関数をつくる
CloudinaryのリサイズURLを生成するget_resized_image_url
という関数を作成します。
引数にはトランスフォーメーション情報となる4つのパラメータを受け取れるようにしました。
ここで配信URLの形式をもう一度確認してみましょう。<transformations>
が<asset_type>/<delicery_type>
の後ろに記載する必要があることがわかります。
https://res.cloudinary.com/<cloud_name>/<asset_type>/<delivery_type>/<transformations>/<version>/<public_id>.<extension>
そのため、<asset_type>/<delicery_type>
に該当する/image/upload/
を起点にしてURLを一度分割してから、URLを再整形するやりかたを取っています。
def get_resized_image_url( image_url, width=None, height=None, crop="fill", gravity="auto" ): """ Cloudinaryの画像URLをリサイズ用URLに変換する処理 Args: image_url (str): 元の画像URL width (int): リサイズ後の幅(オプショナル) height (int): リサイズ後の高さ(オプショナル) crop (str): クロップ方法(デフォルトは"fill") gravity (str): クロップ位置(デフォルトは"auto") Returns: str: リサイズ後の画像URL """ if not image_url: return None # URLを分割してリサイズ用のトランスフォーメーションを挿入 parts = image_url.split("/image/upload/") if len(parts) != 2: # URLの形式が期待と異なる場合、元のURLを返す return image_url # 動的なリサイズ設定(引数のパラメータを含める) transformation_list = [f"c_{crop}", f"g_{gravity}"] if width is not None: transformation_list.append(f"w_{width}") if height is not None: transformation_list.append(f"h_{height}") # リサイズ設定用の文字列作成 transformation_str = ",".join(transformation_list) # リサイズ後のURL整形 resized_url = f"{parts[0]}/image/upload/{transformation_str}/{parts[1]}" return resized_url
② ビューからリサイズ済みのURLをテンプレートに渡す
リサイズ済みのURLを作成する関数ができたので、各ツイートの持つimage
に対してURLの変換処理をかけていきます。私の場合は、150×150ピクセルの画像を表示するようにしました。
- tweets/views.py
class TimelineView(ListView): model = Tweet template_name = "tweets/index.html" context_object_name = "tweet_list" def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) # 各ツイートに対してリサイズ済みの画像URLを設定 for tweet in context["tweet_list"]: if tweet.image: tweet.resized_image_url = get_resized_image_url( tweet.image.url, 150, 150 ) return context
- templates/tweets/index.html
{% for tweet in tweet_list %} <div>{{ tweet.user.username }}</div> <div>{{ tweet.content }}</div> <img src="{{ tweet.resized_image_url }}" alt="投稿画像}"> {% endfor %>
実際のアプリでの見た目↓
おわりに
Cloudinaryを使えば、画像を必要な形式、スタイル、寸法に変換できることが分かりました。画像を最適化することで、ファイルサイズを最小限に抑えながら配信できるので、今後使う機会があれば積極的に活用していこうと思います。 ここまで読んでいただきありがとうございます!
参考記事
https://cloudinary.com/documentation/image_transformations cloudinary.com