外部リソースの画像を静的生成する(静的サイト用)

公開日:2023/2/9更新日:2023/2/9

静的サイト(SSG)から直接画像を配信するには(例:src="/images/sample.jpeg")、staticpublicのフォルダに画像を入れるだけでOKですが、CMSからコンテンツを取得して動的にページを生成する場合、外部リソースからの配信になってしまうため(例:res.example.com/123456/images/sample.jpeg)、外部リソースを元に静的生成するニーズがあります。

バックエンド側のリソースを使いたくない場合や、ホスティングサーバー側で帯域制限を気にしなくていい場合など、利用するケースは意外と多いと思うので、静的生成の基本的な流れをご紹介します。

全体コードは以下の通りです。このコードを各静的サイトジェネレーターで使えるようにカスタマイズすればOKです。

import fs from "fs/promises";
import sharp from "sharp";

export default async function () {
  // 外部リソースの画像をデータとして取得
  const sourceUrl =
    "https://blog.example.com/wp-content/uploads/2023/12/sample.jpeg";
  const res = await fetch(sourceUrl);
  const arrayBuffer = await res.arrayBuffer();

  // 画像の保存場所(書き出し場所)を確保
  const fileDir = "./dist/blog/static-images";
  await fs.mkdir(fileDir, { recursive: true });

  // sharpのインスタンスを作る
  const sharpImg = sharp(Buffer.from(arrayBuffer));

  // 画像の保存(書き出し)
  const [filename] = sourceUrl.split("/").reverse(); // この場合は「sample.jpeg」がfilenameになる。
  const filePath = `${fileDir}/${filename}`; // ./dist/blog/static-images/sample.jpeg
  await sharpImg.toFile(filePath);
}

以下コードの詳細です。

外部リソースの画像データを取得する

画像データの取得にはnode.js環境で実行可能なfetchaxiosを使います。以下はfetchでデータを取得するコードです。

const sourceUrl =
  "https://blog.example.com/wp-content/uploads/2023/12/sample.jpeg";
const res = await fetch(sourceUrl);
const arrayBuffer = await res.arrayBuffer();

画像の保存場所を確保する

静的サイトのどこから画像を配信するか決め、その場所を用意します。

例として、静的サイトのビルド後のフォルダがdistなら./dist/blog/static-imagesのように画像保存用のフォルダを作り、その中に画像を書き出していきます。

フォルダを作成するコマンドはfs.mkdir()で、Promiseが帰ってくるのでawaitでフォルダが作成されるのを待ちます。

fs.mkdir()は指定の場所にフォルダが存在している場合にエラーになるので、フォルダが存在しているかどうかを事前に確認しないといけませんが、オプションのrecursivetrueにすると、その確認を省略することができます。

コードは以下の通りです。

const fileDir = "./dist/blog/static-images";
await fs.mkdir(fileDir, { recursive: true });

sharp(シャープ)のインスタンスを作る

sharpモジュールで画像データからsharpのインスタンス(オブジェクト)を作ります。

npm install sharp

でインストールした後、以下のコードを記述します。

const sharpImg = sharp(Buffer.from(arrayBuffer));

今回は、取得した画像データをそのまま書き出すためsharpを使う必要はないのですが、AVIFWebPなどの次世代の拡張子に変換して画像を軽量化したり、リサイズができるようになるので、基本的にはsharpモジュールを使います。

リサイズ機能はsrcset用に複数のサイズを用意する時に便利で、サイズ(解像度)が1番大きい画像データを一つだけ取得して複数のサイズにリサイズする用途で重宝します。

外部リソースから異なるサイズの画像データをすべて取得すると、アクセスが集中してエラーになったりするのでご活用ください。

画像の保存(書き出し)

画像の書き出しにはtoFile()を使います。引数には書き出し場所(ファイルパス)を指定し、Promiseが返ってくるため、awaitで書き出しが終わるのを待ちます。

すでに画像の保存フォルダ(例:./dist/blog/static-images)は用意しているので、この下にファイル名(例:sample.jpeg)を指定して書き出します。今回の例だと、ファイルパスは./dist/blog/static-images/sample.jpegになります。

以下は、URLから動的にファイル名を抽出して画像を書き出すサンプルコードです。

const [filename] = sourceUrl.split("/").reverse(); // この場合は「sample.jpeg」がfilenameになる。
const filePath = `${fileDir}/${filename}`; // ./dist/blog/static-images/sample.jpeg
await sharpImg.toFile(filePath);

まとめ

主要な静的サイトジェネレーターには、ビルド後やページ生成時など特定のタイミングで処理を実行する機能(hooks)があるので、そのタイミングで今回ご紹介した画像の静的生成をするのが基本です。(例:Nuxt3で画像を静的生成する方法

自前で実装する必要がある場合にぜひご活用ください。