Hugo で記事毎に画像ファイルを管理する

Hugo で記事ファイル毎に画像ファイルを管理する方法を紹介します。その過程で Hugo の HTML 生成ルールも調べたので併せて紹介します。

やりたいこと

Hugo で記事内で画像ファイルを使用する際、/static ディレクトリ内に img ディレクトリ等画像管理ディレクトリを作って、その中に画像ファイルを配置し、各記事から /img/image01.png のように参照することで画像ファイルを使用することができます。

ただ、この方法では記事数が増えて、画像ファイル数が多くなってくると管理がしづらくなってきます。画像管理ディレクトリ内でさらに記事毎にディレクトリを作って管理することもできますが、記事ファイルと同じディレクトリで管理できたほうが楽ですよね。というわけで記事毎に画像ファイルを管理する方法について調べてみます。

環境

OS
Windows 10 21H2
Hugo
v0.101.0

Hugo の HTML 生成のルール

みなさんは記事ファイル (Markdown) のファイル名はどのようにつけていますでしょうか。私はもともと /posts/something-article.md (/posts はコンテンツルート) のように記事名を記事ファイルにつけていたのですが、このような状態で hugo コマンドで HTML を生成すると /public/posts/something-article/index.html のように生成され、記事ファイル名はディレクトリになります。

なぜこのような動きになるかについては、恐らくブラウザで URL 表示させる際に最後に .html を見せたくないのかなと考えています。Web Server の設定によりますが、例えば Apache Http Server だと DirectoryIndex ディレクティブの設定で、URL のファイル名が省略されている場合は、index.html を表示させる設定が可能です。その結果、上記の記事には /posts/something-article/ という URL パスでアクセスすることが可能です。(/public ディレクトリ以下を Web Server で公開している前提なので /public はなくなる)

記事毎にディレクトリを分けて記事ファイルと画像ファイルを管理

NG な配置

前節の通り、私は記事ファイル名に記事名をつけていたのですが、記事毎にディレクトリを分けるにあたって、ディレクトリ名にも記事名をつけました。つまり記事ファイルは /posts/something-article/something-article.md のように配置し、さらに something-article ディレクトリ内に img ディレクトリを作り、そのディレクトリ内に記事で使用する画像を配置しようと考えました。

具体的には以下のような構成です。

/
└ posts/
  └ something-article/
    ├ something-article.md
    └ img/
      └ image01.png

記事内からの画像ファイル参照は、同じ階層にあるので、./img/image01.png でリンクできそうですよね。 ところが前節の通り、この構成で HTML を生成すると以下のような構成になってしまいます。

/
└ public/
  └ posts/
    └ something-article/
      ├ something-article/
      │ └ index.html
      └ img/
        └ image01.png

記事ファイル名から something-article というディレクトリが生成され、記事 HTML はその中に配置され、画像を配置した img ディレクトリの階層がずれ、画像ファイルは ../img/image01.png と参照しないとリンクできません。

記事ページの URL パスも記事ファイル名が 2 回も含まれ、イケてないです。

いい感じの配置

Hugo には Page Bundle という、ページのリソースをグループ化する概念があり、Page Bundle は Leaf Bundle と Branch Bundle に分けられます。Leaf Bundle は URL 末端の記事ページにあたるイメージで、Branch Bundle は記事の一覧のページなどの中間ページのイメージです。

詳細は Page Bundles | Hugo に記載がありますが、Page Bundle はそのページで資料するリソースを同ディレクトリに配置することが可能です。公式には以下の配置のように、あくまで同階層に各リソースファイルを配置する前提で書かれています。

/
└ posts/
  └ something-article/
    ├ index.md
    └ image01.png

Page Bundle 内のサブディレクトリについては言及されていませんが、Branch Bundle ではサブディレクトリは Page Bundle やセクションとして扱われるので、それ以外の用途としてのディレクトリは扱えません。一方 Leaf Bundle では Page Bundle 等のネストは禁止されているので、サブディレクトリを Hugo 的に取り扱うことはないため、サブディレクトリを配置しても悪さはしません。したがって結果的に、Leaf Bundle では画像ファイル管理用としてサブディレクトリを扱うことができます。

肝心の Page Bundle の使い方ですが、index.md が置かれているディレクトリは Leaf Bundle になります。そして _index.md が配置されているディレクトリは Branch Bundle になります。

これらを踏まえ最終的に記事ファイルと画像ファイルのディレクトリ構成は以下のようにしました。

/
└ posts/
  └ something-article/
    ├ index.md
    └ img
      └ image01.png

この構成だと HTML を生成すると以下のような構成で出力されます。

/
└ public/
  └ posts/
    └ something-article/
      ├ index.html
      └ img/
        └ image01.png

記事ファイル名が index なので、新たに記事ファイル名のディレクトリが生成されることなく、記事ファイルと img ディレクトリが同じ階層で出力されます。記事ページの URL パスも /posts/something-article/ となり申し分なしです。

あとがき

なんとなく Hugo を使っていましたが、Page Bundle という概念を今回初めて知りました。画像ファイルを記事毎に管理したかっただけだったのですが、Hugo について思わぬ勉強になりましが。Branch Bundle も使いこなせば、一覧系のページもセクション毎にカスタマイズできそうに見えたので、機会があったら試してみたいです。