tag:crieit.net,2005:https://crieit.net/tags/OGP%E8%8A%B8/feed 「OGP芸」の記事 - Crieit Crieitでタグ「OGP芸」に投稿された最近の記事 2019-10-17T05:05:57+09:00 https://crieit.net/tags/OGP%E8%8A%B8/feed tag:crieit.net,2005:PublicArticle/15487 2019-10-17T05:05:57+09:00 2019-10-17T05:05:57+09:00 https://crieit.net/posts/fly-io-SPA-OGP-package-json fly.ioでSPA&OGP芸を最速かつゼロ依存でやる(package.jsonすら不要) <p>個人開発界隈ではOGP芸というものが流行っているみたいですね。</p> <p>SNSにシェアする際に表示されるOGPの設定や画像をいい感じにするみたいなことのことですが、SPAではこれを生成するのが割と面倒だったりします。</p> <p>自分が作っている<a target="_blank" rel="nofollow noopener" href="https://www.g-g-g-g.games">g4</a>でもOGPを使ったシェアを実装していますが、SSRを使って配信していて、割と設定が面倒だったりしています。</p> <p>こんなの<br /> <a href="https://crieit.now.sh/upload_images/0f6a4a0608c5dc3550a5c44abd805d7e5da777f12f09e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0f6a4a0608c5dc3550a5c44abd805d7e5da777f12f09e.png?mw=700" alt="68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f35313430322f63356465616336612d333663642d366131342d646434302d3139333036306636353732612e706e67.png" /></a></p> <p>今回、なるべくSSRをしないでSPAのままやってみることはできないかというのをfly.ioで試してみました。</p> <h1 id="作ったものの概要"><a href="#%E4%BD%9C%E3%81%A3%E3%81%9F%E3%82%82%E3%81%AE%E3%81%AE%E6%A6%82%E8%A6%81">作ったものの概要</a></h1> <ul> <li>実際にOGP画像として表示される画像は<code>/image.png?i=0</code>で配信する。</li> <li><code>/</code>に<code>?i=0</code>のようなパラメータをアクセスすると、入力した↑を含むOGPが反映されたhtmlを表示する</li> <li>fly.ioが日本語に対応してないので、titleに日本語は使えない</li> <li>せっかくfly.ioを使ってますが、今回はOGPだけにフォーカスしたいのでキャッシュ周りは実装してません</li> </ul> <h1 id="作ったもの"><a href="#%E4%BD%9C%E3%81%A3%E3%81%9F%E3%82%82%E3%81%AE">作ったもの</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://og-image-sample.edgeapp.net?i=0">https://og-image-sample.edgeapp.net?i=0</a></p> <p>or</p> <p><a target="_blank" rel="nofollow noopener" href="https://og-image-sample.edgeapp.net?i=1">https://og-image-sample.edgeapp.net?i=1</a></p> <p>これをtwitterやfacebookなどでシェアしてみてください。</p> <h1 id="作り方"><a href="#%E4%BD%9C%E3%82%8A%E6%96%B9">作り方</a></h1> <p>事前にflyのcliをPCにインストールしておく<br /> npmは必要ないです(あったほうが便利だけど最速なので)</p> <p>必要なファイルは4つだけ<br /> - index.js: なんか処理書くやつ<br /> - template.html: テンプレートです。ここに情報を流し込む<br /> - fly.yml: なんか設定書くやつ<br /> - sample.svg: OGP芸したい適当なsvg。今回はURLによって内容を書き換えるため雑に<span>{</span><span>{</span>title<span>}</span><span>}</span>みたいなやつをここに書いて、テキスト置換でそこに内容を埋め込んでます。</p> <p>``` html:template.html<br /> <!DOCTYPE html><br /> <html lang="ja"><br /> <head><br /> <meta charset="utf-8" /><br /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /><br /> <meta name="theme-color" content="#000000" /><br /> <title>fly.ioで最速ogp芸</title><br /> <meta name="description" content="by shwld" /><br /> <meta name="twitter:card" content="summary_large_image" /><br /> <meta name="twitter:creator" content="@shwld" /><br /> <meta property="og:type" content="website" /><br /> <meta property="og:url" content="https://www.g-g-g-g.games" /><br /> <meta property="og:title" content="shwld" /><br /> <meta property="og:description" content="shwld" /><br /> <meta property="og:image" content="https://www.g-g-g-g.games/assets/image.png" /><br /> <meta property="og:image:alt" content="shwld" /><br /> </head><br /> <body><br /> <h1 id="fly.ioでogp芸をする場合のサンプルだよ"><a href="#fly.io%E3%81%A7ogp%E8%8A%B8%E3%82%92%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%AE%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%81%A0%E3%82%88">fly.ioでogp芸をする場合のサンプルだよ</a></h1><br /> </body><br /> </html></p> <pre><code><br />templateはこんな感じになりました。 ogpのcontentは置き換えるので何でもいいです。とりあえずさり気なくg4のものを入れてアピールしておきます。 次に`index.js`です。こちらにすべての処理を書きます。 ``` js:index.js import { Image } from '@fly/image' import { mount } from "@fly/fetch/mount" // templateをテキストで取得するFunction async function getTemplate() { const resp = await fetch("file://src/template.html") return await resp.text() } // svgをテキストで取得しつつ中身ちょっと埋め込めるFunction async function getSvgText(title) { const resp = await fetch("file://src/sample.svg") const text = await resp.text() // g4(https://www.g-g-g-g.games)ではReactでテキスト化されたsvgを吐いてるが、とりあえず単純に置換する return text.replace('<span>{</span><span>{</span>content<span>}</span><span>}</span>', title) } // 画像形式のレスポンス作るFunction async function responseImage(svgText) { const svgResp = new Response(Buffer.from(svgText)) const buf = await svgResp.arrayBuffer() const png = new Image(buf).png() const result = await png.toBuffer() return new Response(result.data, { headers: { 'Content-Type': 'image/png', 'Content-Length': result.data.byteLength.toString(), } }) } // 出力データのパターン const TITLES = [ 'g4 is pomodoro rpg!', 'fly.io de OGP!!', ] // fly.ioのrouterみたいなやつ。ここに処理を書いてく。 const mounts = mount({ // このパスでogpの画像を生成する '/image.png': async (req, init) => { const url = new URL(req.url) // URLからQueryStringを取得 const index = url.searchParams.get('i') if (index !== '0' && index !== '1') { return new Response('not found', { status: 404 }) } // 対応したタイトルを取得 const title = TITLES[index] // タイトルをsvgに埋め込んだテキストを作る const svgText = await getSvgText(title) // svgからpng画像を生成する return responseImage(svgText) }, // このパスをシェアする '/': async (req, init) => { const url = new URL(req.url) // URLからQueryStringを取得 const index = url.searchParams.get('i') if (index !== '0' && index !== '1') { return new Response('not found', { status: 404 }) } // 対応したタイトルを取得 const title = TITLES[index] // テンプレートをparseして編集できるようにする const doc = Document.parse(await getTemplate()) // テンプレートにOGPを埋め込む doc.querySelector('meta[name="description"]').setAttribute('content', title) doc.querySelector('meta[property="og:url"]').setAttribute('content', url.href) doc.querySelector('meta[property="og:title"]').setAttribute('content', title) doc.querySelector('meta[property="og:description"]').setAttribute('content', title) doc.querySelector('meta[property="og:image"]').setAttribute('content', `${url.origin}/image.png?i=${index}`) doc.querySelector('meta[property="og:image:alt"]').setAttribute('content', title) // htmlを返す return new Response(doc.documentElement.outerHTML, { headers: { 'Content-Type': 'text/html' }, status: 200, }) }, }) // リクエストをmountsの定義を使って処理するように設定する fly.http.respondWith(mounts) </code></pre> <p>index.jsはこんな感じ。<br /> <code>/</code> は<code>template.html</code> のogpを書き換えて出すだけ。<br /> <code>/image</code> はsvgファイルを読み込んで文字列を置換したものをpngに変換して返してます。</p> <p>fly.ymlやsvgファイルは特に特別な設定はないのでドキュメントや最後にソースを貼るのでそちらを見ていただければ。</p> <p>あとは、fly.ioのコンソールでアプリを作って、<code>fly deploy</code> するだけ。</p> <p>再度になりますが、</p> <p><a target="_blank" rel="nofollow noopener" href="https://og-image-sample.edgeapp.net?i=0">https://og-image-sample.edgeapp.net?i=0</a></p> <p>or</p> <p><a target="_blank" rel="nofollow noopener" href="https://og-image-sample.edgeapp.net?i=1">https://og-image-sample.edgeapp.net?i=1</a></p> <p>これをtwitterやfacebookなどでシェアしてみてください。</p> <p>以上になります。ソースは<a target="_blank" rel="nofollow noopener" href="https://github.com/shwld/fly.io-og-image">こちら</a>にあります。</p> shwld