OGP画像をLambda+Vueで描画する

以下の画像のようにLambda+VueでOGP画像を生成してS3に保存する仕組みを作ったので記録を残します。

Lambda上でVueを描画してS3に保存している図

これはContentfulというヘッドレスCMSを使用してメディア(セリフデータベース)を作成した際の副産物になります。

何ができるのか?

LambdaにPOSTリクエストを送ると、以下赤枠の様なOGP画像を生成してS3に保存します。
OGP画像のデザインはVueコンポーネントとしてカスタマイズできます。凝ったデザインのOGP画像も作成できるのが強みです。
また、送信したテキストをOGP画像に埋め込んだり出来ます。

ライブラリとして切り出しました

serverless-vue-ogp-rendererに置いています。
READMEにしたがってコマンドを打てばローカルで動作確認できるようになっています。
よかったら参考にしてみてください。

Serverless Frameworkについて

Serverlessフレームワーク を使用する事で、簡単にLambdaアプリケーションが作成できます。
今回作成したツールはこのServerlessフレームワークを使用しています。


Serverlessフレームワークとは、サーバーレスな環境にアプリケーションを構築/デプロイできるフレームワークで、AWSやGCP, Azureなどのプラットフォームをサポートしています。

YAMLベースの設定ファイルを記述しコマンドを実行すると環境が構築され、アプリケーションがデプロイされます。

今回使用した設定ファイルは以下の通りです。(ファイルはこちら

service: serverless-vue-ogp-renderer

provider:
  name: aws
  runtime: nodejs12.x
  stage: prod
  region: ap-northeast-1

functions:
  app:
    handler: index.handler
    events:
      - http: POST /render

plugins:
  - serverless-offline
  - serverless-dotenv-plugin

/renderにリクエストを送信するとindex.jshandler関数が呼び出されるようになっています。
serverless-offlineプラグインを使用してローカルで動作確認できるようにしています。
serverless-dotenv-plugin.envファイル(環境毎の設定)をロードできるようにしています。

Puppeteerについて

puppeteerはプログラムからchromiumを操作できるライブラリです。

puppeteer経由でchromiumを操作して、Vueを描画&画像として保存します。

1点ハマりポイントがありまして、lambda上で使用する場合、ファイルサイズの問題で通常のchromiumが使用できません。
なので今回は、lambda向けに軽量化されたchromium(chrome-aws-lambda)を使用しました。

自分はVueComponentRendererというクラスを作成してchromiumに関する処理をまとめました。(ファイルはこちら

いくつかかいつまんで説明します。

chromiumを起動する

        const browser = await chromium.puppeteer.launch({
            args: chromium.args,
            executablePath: await chromium.executablePath,
            headless: chromium.headless,
            ignoreHTTPSErrors: true,
            defaultViewport: {
                width,
                height,
            }
        });

ページを開く

あらかじめ、Vueコンポーネントを描画するhtmlを用意しておき、それを開きます。

await page.goto('file://' + dir + '/renderer/dest/renderer.html', {waitUntil: 'networkidle2'});

Vueにパラメータを渡す

リクエストで送信されたテキストをVueに渡しています。
Vueのコンポーネント側では$bodyをwatchしており、値が設定されるとコンポーネントが再描画される仕組みになっています。

await page.evaluate((body) => {
            window.vue.$body = body;
        }, reqBody);

スクリーンショットを撮影する

await page.screenshot({
            path: outPath,
            clip: {
                x: 0,
                y: 0,
                width,
                height
            }
        });

S3への画像の保存について

aws-sdkを使用しています。

今回はS3Repositoryというクラスに処理をまとめました。(ファイルはこちら

特に珍しいものはないので解説はしません。

おわりに

最後に、処理の大枠であるindex.jsをざっくり紹介すると、以下の画像のようになります。

副作用(※)を伴う処理をクラスとして切り出しただけですが、ひと目見れば大筋の処理が分かるコードになりました。
※通信やディスクIOを伴う処理の事

副作用を別のクラスに切り出す事でユニットテストが掛けるようにもなるので、こういう書き方があたりまえになるといいなぁと常々願ってます。(仕事で2000行超えのファイルに苦しめられながら)

以上です!

コメントする