Next.jsのブログにInstagramストーリーズ向けシェア機能を実装したので振り返り
投稿日: 2024年10月02日
Instagramでは、Xのような公式のシェア機能がありません。
ですが、僕のスクールは一応Instagramから始まったのもあり、できればInstagramでシェアしやすい仕組みが欲しいなと思いました。
Instagramで手軽に投稿というとストーリーズなので、
こんな機能をNext.jsで作ってみました。
実装方針としては、
「Next.js内に隠しURLを作って、そこにストーリーサイズで記事情報が表示されるページを作り、そのページをスクショしてダウンロードする」
という処理を行うことにしました。
動的なOGP画像を生成する際などに、よく使われる手法です。
まず、隠しURLを作って、ストーリーズサイズ(縦長の1920px - 1080px)で記事情報を表示させるページを作ります。
このページが、後にスクリーンショットを撮る対象となります。
このページは以下のURLに実装しています。
https://shiftb.dev/story_images/n_ziqaqgh
次に、バックエンドでWebブラウザを立ち上げ、上記ページにアクセスし、スクリーンショットを撮るためのAPIを作成しました。
以下のライブラリを使用しています。
ChromiumとはGoogle Chromeのオープンソース版のブラウザです。今回のようなWebページのスクショを撮ったり、スクレイピングでWebサイトから情報を抜き取る際によく使います。
通常、サーバー側でブラウザを操作するのはかなり重たい処理です。AWS Lambdaのようなサーバーレス環境ではリソースが限られているため、軽量化されたChromiumが使用されます。@sparticuz/chromiumは、この軽量なChromiumをサーバーレス環境でも動かせるようにするためのライブラリです。
以下、これらのライブラリを用いて書いたコードです。
import chromium from "@sparticuz/chromium";
import { NextRequest, NextResponse } from "next/server";
import puppeteer from "puppeteer-core";
export async function GET(
_request: NextRequest,
{ params: { id } }: { params: { id: string } }
) {
// ブラウザを起動
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath(),
headless: chromium.headless,
ignoreHTTPSErrors: true,
});
// ブラウザのタブを開く
const page = await browser.newPage();
// スクショ対象のページにアクセス
await page.goto(`${process.env.NEXT_PUBLIC_APP_BASE_URL}/story_images/${id}`);
// ページの表示が完了するまで待つ
await page.waitForSelector("body", { visible: true });
// サイズを指定してスクショを撮ってbase64方式の画像データを取得
const image = await page.screenshot({
encoding: "base64",
fullPage: false,
clip: { x: 0, y: 0, width: 1080, height: 1920 },
});
// ブラウザを閉じる
await browser.close();
// 取得した画像データをクライアントに返す
try {
return NextResponse.json({ status: "OK", image }, { status: 200 });
} catch (error) {
return NextResponse.json(error);
}
}
フロントエンドでは、このAPIから取得した画像をユーザーに表示します。
ユーザーはこの画像を、PCなら右クリック、スマホなら長押しでダウンロードし、Instagramのストーリーズとしてシェアすることができます。
こんな感じです。
こちらもShiftBのリポジトリで見れるので、動的に画像を生成みたいな機能を作りたい方は、参考にしてみてください。