🍅

サイト内画像をwebpに移行し始めました

2023/08/15に公開

はじめに

株式会社ウェイブCoolmicのエンジニアをしている布施です。
Coolmicは日本のコミックを海外向けに配信するWEBサービスです。

先日サイトパフォーマンス改善のために、サイト内で表示する画像のwebp化を始めました。
その中で実際に行った作業をご紹介します。
https://developers.google.com/speed/webp?hl=ja

要件

今回の要件は大きく分けて以下の2つでした。

  1. サイト内で表示する画像(jpg/png)をwebpに差し替える
  2. webpの表示に対応していないブラウザでは、従来通りjpg/png画像を表示する

画像をwebpに変換する

2つ目の要件のため、画像はjpg/pngとwebpの両方を用意しておく必要がありました。
普段、画像はAWSのS3に格納したものをCloudFront経由で配信しています。
そこで、画像をS3に格納した時にLambdaを実行し、webp形式のコピーの作成と専用のバケットへの格納を自動で行うようにしました。
これにより、画像を用意するフローは従来から変更する必要がなくなります。
念の為エラー通知もします。

S3にアップロードされたファイルには以下のようにアクセスできます。

from urllib.parse import unquote_plus

# 呼び出しレコード。eventはLambda関数ハンドラーの引数
record = event['Records'][0]

# 画像がアップロードされたバケットの名前
bucket = record['s3']['bucket']['name']

# 画像ファイルのS3上のパス
key = unquote_plus(record['s3']['object']['key'], encoding='utf-8')

Lambda実行環境ローカル⇄S3間のダウンロード、アップロードは以下。

import boto3

s3_client = boto3.client('s3')

# 画像をダウンロード
s3_client.download_file(bucket, key, tmp_path)

# 画像をアップロード
s3_client.upload_file(tmp_path, bucket, key)

jpg/png→webpの変換は以下のコードでできます。

from PIL import Image

img = Image.open(input_file_path)
img.save(output_file_path, 'webp')

上記のコードをハンドラにまとめて記述し、デプロイします。
一連の処理の中で発生したエラーに気づくために以下のような形式にしました。

def handler(event, context):
    try:
        main(event, context)
    except Exception as e:
        notify_error_message(str(e)) # エラー通知
        print(e)

また、後続の作業のために変換後のwebp画像のファイル名はhoge.jpg.webp/hoge.png.webpとなるようにしました。

画像を参照するパスを修正する

画像を準備したら、次はフロントエンドで画像を参照している部分を修正します。
ここで、Modernizrを利用してブラウザがwebpに対応しているかどうかを判定します。
https://modernizr.com/
Modernizrのダウンロードページでは、判定したい機能を選択することでカスタマイズされたjsファイルをダウンロードすることができます。
今回はWebpを選択しました。
Coolmicではダウンロードしたmodernizr-custom.jsを以下のように利用しました。

import '@hoge/modernizr-custom';

export default function getWebpExtension() {
  return new Promise((resolve) => {
    Modernizr.on('webp', (result) => {
      if (result) {
        resolve('.webp'); // webp対応ブラウザの場合はファイル名末尾に".webp"をつける
      } else {
        resolve(''); // webp非対応ブラウザの場合は何もつけない
      }
    });
  });
}

コード上で画像ファイルのパスを指定している箇所にgetWebpExtension()を追記することで、
ブラウザがwebpに対応している場合のみ拡張子.webpを追加します。
webp画像のファイル名はhoge.jpg.webp/hoge.png.webpになっているので、
これで画像の出し分けは完了です。

おまけ

ブラウザによって画像を出し分ける方法として、CloudFront Functionsを利用するものも検討していました。

上記のModernizrの処理をCloudFront Functionsで定義し、
CloudFrontのビヘイビアに設定するだけで作業は終わりです。
この方法だとフロントエンドのコードを変更する必要もありません。
しかし、リクエスト数に応じて料金がかかることとあまり重い処理はできないということから、
今後の運用を考えて断念しました。
また別の機会があれば使ってみたいと思います。

最後に

今回は業務フローを複雑化することなくパフォーマンス改善ができたので良かったと思います。
(フローの効率化もできたらもっといい!)
今後も適切に自動化を取り入れてサービスや業務の改善を行っていきたいです。

宣伝

株式会社ウェイブでは、電子コミックやアニメ配信サービスなどを自社開発で運営しております。
新しい技術を積極的に取り入れているモダンな環境が整っているウェイブで一緒に働いてみませんか?
興味ある方は是非こちらをご覧ください!

https://recruit.wwwave.info/

wwwave's Techblog

Discussion