🦁

Node.jsで特定のdirの画像をBase64形式にエンコードしてそのファイル一覧を配列としてビルド時に定義しておく

2022/11/19に公開約3,100字

モチベーション

自前で動的OGPを実装し、画像背景を<image x="0" y="0" width="1200" height="630" xlink:href="${base64}" />というふうにbase64形式のテキストを使用して描画している
動的OGP出力のエンドポイントが叩かれるたびに画像をエンコードしてreadFileSyncして、とやってもいいけどほんの少しでも処理を早くするためにアプリケーション実行前段階で事前に画像をtxtとして用意しておきたい
また、そのtxt各ファイルも物量が多いと読み込みも時間がかかるので事前に配列として定義しておき、indexに乱数指定で取り出すこととかもできるようにしたい

とはいえ、今時天下のVercel様がog-imageを公開してくれているのでそちらを使った方がよっぽど早いのではなかろうかという微妙な気持ちではいる

ディレクトリ構成とやりたいこと

assets
└── images
    ├── hoge1.jpg
    ├── hoge2.jpg
    └── base64
        ├── # ここにbase64形式のhoge1.txtを作りたい
        └── # ここにbase64形式のhoge2.txtを作りたい

ビルド時にimages/*をimages/base64/に同じファイル名でtxtとして保存してからデプロイしたい

また、定数用のフォルダとファイルを生やして、

src
├── app.ts
├── consts
│   ├── backgroundImagePaths.ts # これ
│   └── index.ts
# ...

assets/images/base64の各txtファイルのパスを配列として持っておきたい

コード

// scripts/base64Encode.ts

import fs from 'fs'
import glob from 'glob'

function base64Encode() {
  glob('assets/images/*', (err, files) => {
    files.forEach((file) => {
      if (!file.match(/.(jpg|jpeg|png|gif)$/)) return
      // base64に変換する
      const bitmap = fs.readFileSync(file)
      const base64 = 'data:image/jpeg;base64,' + Buffer.from(bitmap).toString('base64')
      // 元々のファイル名.txtにする
      const fileName = file
        .split('/')
        .pop()
        ?.replace(/\.[^/.]+$/, '.txt')
      // 書き出し
      fs.writeFileSync(`assets/images/base64/${fileName}`, base64)
    })
  })
}

base64Encode()

このtsファイルを実行することで 👇こんな感じで

jpgをtxtとして持っておける

また、

// scripts/outputBackgroundImagePaths.ts

import fs from 'fs'
import glob from 'glob'

function outputBackgroundImagePaths() {
  let content = `const backgroundImagePaths: string[] = [`

  glob('assets/images/base64/*', (err, files) => {
    files.forEach((file) => {
      // .txt以外は無視
      if (!file.match(/.txt$/)) return
      // contentにfileを追加していく
      content += `'${file}',`
    })
    content += `];\n\nexport default backgroundImagePaths`
    fs.writeFileSync(`src/consts/backgroundImagePaths.ts`, content)
  })
}

outputBackgroundImagePaths()

このファイルを実行することで 👇こんな感じで

配列の中に定義できた

実行準備

後はpackage.jsonのビルドコマンドの中にts-node scripts/base64Encode.tsとか含めればGitHubActionsなどのCI上で実行もできるし、Git管理したければhuskyのpre-commit時にでもこのファイル実行するよう設定すれば良さそう

*.txtがなければscripts/outputBackgroundImagePaths.tsはうまく機能しないため、同期的に処理する必要がある
なので、先にtxtへのエンコード処理 👉 配列として定義 という順番でやっていくことにした

// package.json
{
  // ...
  "setup:background-image": "ts-node scripts/base64Encode.ts && ts-node scripts/outputBackgroundImagePaths.ts && npx prettier --write src/consts/backgroundImagePaths.ts",
}

ついでにprettierも設定した

後はbuildコマンドの中で

// package.json
{
  // ...
  "build": "npm run setup:background-image && 何かしらのビルドコマンド",
}

先ほど生やしたコマンドをコンパイル前の段階に追記してあげればいい

また、今回使用したスクリプトは事前準備用のスクリプトなのでコンパイルする必要はないのでtsconfigのexcludeなどにでも追加しておく

// tsconfig.json

{
  "compilerOptions": {
    // ...
  },
  "exclude": [
    "./scripts/*",
  ]
}

Discussion

ログインするとコメントできます