前から「Snovel」という駅ノートでつながるSNSを作ってます。
あわせてLP(https://snovel.app)も作りました。
アプリから駅にチェックインしたことがシェアされたときに、共通の画像だとちょっとさみしいので「〜駅でチェックインしよう」とチェックインされた駅に合わせてLPのOGP画像を自動生成されるような仕組みを作ってみました。
実際これを使うにはSSR等の技術が必要になるかと思いますが、SSRについては他の記事を読んでください。
対象読者
- フロントエンド中級者
- Firebase、Cloud Functionsが何か知ってる人
使うもの
Functionsでエンドポイントを作る
アプリのチェックイン時やLP表示時に叩くために、OGP生成関数のエンドポイントを作ります。
index.js
const functions = require("firebase-functions");
// 他の関数もあるので、OGPの関数が呼び出されたときだけ読み込むようにする
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'ogp') {
exports.ogp = functions
.region('asia-northeast1') // 特に使わない理由がないので東京リージョン
.https
.onRequest(require('./ogp')); // onCallとかを使うとCORS対策にもなるらしけどやったことがない
}
ogp.js
const admin = require("firebase-admin");
const spawn = require("child-process-promise").spawn;
const path = require("path");
const os = require("os");
const admin = require("firebase-admin");
admin.initializeApp();
// ご自身で用意してください
const FONT_NOTO = "fonts/NotoSansCJKjp-Bold.otf";
const ogp = async (req, res) => {
// CORS対策
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
return
}
// 必要な変数を定義する
const bucket = admin.storage().bucket();
const STORAGE_ROOT = "https://firebasestorage.googleapis.com/v0/b";
const bucketName = bucket.name;
const uploadPath = 'ogp/PUT_ID_HERE.png';
const dlPath = encodeURIComponent(uploadPath);
const dlURL = `${STORAGE_ROOT}/${bucketName}/o/${dlPath}?alt=media`;
// すでに生成されたファイルがある場合はそのままそれを返す
const uploadedFile = bucket.file(uploadPath);
const alreadyExists = await uploadedFile.exists();
if (alreadyExists[0]) {
res.send(dlURL);
return;
}
// 実際画像に出てくる文言。実際はAPIから取ってきたのを入れたりとか。
const msg = '高田馬場駅にチェックインしよう';
// ベースの画像
const template = 'img/ogpbase.png';
// 生成した画像のパス。tmpディレクトリに配置する
const outFile = path.join(os.tmpdir(), 'generated.png');
// ImageMagicで画像生成
await spawn("convert", [
"-font", FONT_NOTO, // フォントの指定。カスタムフォントの場合はパスを指定
"-pointsize", "48", // フォントサイズの指定
"-fill", "white", // 文字色の指定。白文字に設定
"-gravity", "Center", // 位置の基準を指定。
"-annotate", "+0-16", msg, // 文字の指定。+0+0は位置の基準からの相対位置。
template, // 入力画像のパス
outFile // 出力画像のパス
]);
// Cloud Storageへのアップロード
await bucket.upload(outFile, {
destination: uploadPath
});
// return Download URL
res.status(201).send(dlURL);
}
module.exports = ogp;
割と簡単でしたね。
実際のところは、https://snovel.app/station/1130210のようなURLにアクセスされたり、アプリでチェックインされたときに上記のAPIが叩かれて、シェアするときに一緒にOGPとしてシェアされる感じです。
注意点
割と重い処理なので、関数のメモリ割り当てを上げたり、1200x630を超えるような画像を扱わないようにしてください。