3
2

More than 3 years have passed since last update.

Lambda+API Gateway+CloudFrontとVueでOGP画像の自動生成をする

Posted at

Lambda+API Gateway+CloudFrontとVueを使ってフロントエンドのみでOGP画像の自動生成をしてみたので備忘録。

構成

まずVueでSVGを返すページを用意しておく。
Lambda側はchrome-aws-lambdaでスクリーンショットを撮って、base64で返すようにする。

よくあるLambda@Edgeを使ったダイナミックレンダリングを行いつつ、Edgeで返すMetaタグのog:imagetwitter:imageのURLへのアクセスがあったら、用意しておいたSVGページをLambdaでスクリーンショット撮ってAPI Gateway経由でpngにして返す、というちょっと面倒くさい構成。

バックエンド側でLambdaを起動させてスクリーンショット撮ってS3に保存とかでもよかったのだけど、今回はあくまでもアクセスがあったらOGP画像を返すようにしたかったので、こんな感じの構成にした。

VueでSVG生成

VueでSVGを生成するのはこちらの記事を参考にさせていただいた。
Vue.jsとFirebaseでOGP画像生成系のサービスを爆速で作ろう

<template>
  <div class="hello">
    <svg ref="svgCard">
      <text transform="translate(103.29 347.281)" fill="#e51f4e" font-size="29" font-family="HiraginoSans-W5, Hiragino Sans" letter-spacing="-0.002em">
        <tspan x="0" y="26">{{ data.content }}</tspan>
      </text>
    </svg>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  name: 'Svg',
  data () {
    return {
      data: {}
    }
  },
  beforeMount () {
      this.fetchData(response => {
        this.data = response
      })
  },
  methods: {
    ...mapActions([
        'fetchData',
    ])
  },
}
</script>

svgタグの中にvueのデータを埋め込めるので、APIから持ってきたデータを表示できるようにする。
注意が必要なのが、svgタグではテキストを自動で折り返してくれないので、途中で切って配列にしてv-forで回すとかしないといけない。

あとはこのページをrouterに登録する。

import Svg from '@/views/Svg.vue'

Vue.use(VueRouter)

const routes = [
  ...
  {
    path: '/path/to/svg',
    name: 'Svg',
    component: Svg
  }
]

Lambda+API Gateway

次にスクリーンショットを撮るLambdaを作る。ほんとはserverless frameworkで作りたかったのだけど、serverlessで作ると何故かchromeが動いてくれなかったのと、API Gateway側の設定がイマイチ把握しきれなかったので、今回はコンソールでポチポチした。

Lambda

const chromeLambda = require("chrome-aws-lambda");

const defaultViewport = {
  width: 1200,
  height: 630
};

exports.handler = async event => {

  const browser = await chromeLambda.puppeteer.launch({
    args: chromeLambda.args,
    executablePath: await chromeLambda.executablePath,
    defaultViewport 
  });

  const sleep = (time) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve();
      }, time);
    });
  }

  const page = await browser.newPage();

  const url = "https://" + process.env.DOMAIN + "/path/to/svg";
  await page.goto(url, { waitUntil: "networkidle0" });
  sleep(1000)

  const buffer = await page.screenshot({ encoding: "base64", type: "png" });

  return {
    "statusCode": 200,
    "headers": {"Content-Type": "image/png"},
    "isBase64Encoded": true,
    "body": buffer
  };

};

Lambda側のソースコードはこんな感じ。chrome-aws-lambdaはLambda Layerを使わせてもらった。
https://github.com/shelfio/chrome-aws-lambda-layer#available-regions

API Gateway

API Gateway側は適当なリソースを作って、GETメソッドを用意する。

  • /path/to/svg - GET - 統合リクエストで上で作ったLambdaに繋いで、Lambda プロキシ統合の使用にチェックを入れる
  • HTTP リクエストヘッダーにAcceptを追加する
  • メソッドレスポンスのコンテンツタイプにimage/pngを追加する
  • APIの設定で、バイナリメディアタイプにimage/pngを追加する

APIキーや使用量プランは必要に応じて設定して、ステージにデプロイする。ここでは仮にprodステージにデプロイしたと仮定して進める。

これでcurlコマンドでAPI Gatewayを叩くと画像が返ってくるようになる。


curl -H "Accept: image/png" --output test.png https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/path/to/svg
CloudFront

この状態でブラウザでアクセスすると、Acceptヘッダーがリクエストに含まれないのでjsonが返ってきてしまう。CloudFrontを経由させることで、Acceptヘッダーを付けつつ、一度アクセスのあった画像はキャッシュしてもらえる。

まずDistributionsの作成してOriginを追加する。

Origin Domain Name: xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com
Origin Path: /prod
Origin Custom Headers: Accept: image/png

Behavior側はOrigin or Origin Groupで先ほど追加したOriginを選択する。

CloudFrontのデプロイが完了したら、CloudFrontのURL経由でブラウザで画像が表示されるようになる。

Lambda@EdgeでOGPタグ

Lambda@Edgeを使ったOGPの生成はこのあたりを参考に。
Lambda@EdgeでSPAのOGPを動的に設定する
SSRをやめる。OGP対応はLambda@Edgeでダイナミックレンダリングする。

今回は1個目の記事のような感じで、botからのアクセスだった場合はバックエンドのAPIにアクセスしてタイトルとかを整えつつ、上で用意した画像のURLを含んだOGPタグを生成して返すようにした。

botの種類はこのあたり。


const crawlers = [
  "Googlebot",
  "facebookexternalhit",
  "Twitterbot",
  "bingbot",
  "msnbot",
  "Slackbot",
  "Discordbot"
];

まとめ

とまあ、こんな具合でOGP用の画像を自動生成できた。あとは必要に応じてコールドスタート対策もしておきたいところ。
Serverless Frameworkで行うLambdaのコールドスタート対策

いつもながら先人の知恵や知識に感謝。

参考URL

https://scottbartell.com/2019/03/25/automating-og-images-with-aws-lambda/
https://codissimo.sinumo.tech/2019/12/27/serverless-puppeteer-with-aws-lambda-layers-and-node-js/
https://qiita.com/junara/items/5563ad7ee133ce736ed0
https://qiita.com/kodai-saito/items/9051d2b30a29c7d64f7d

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2