Astroでギャラリー

作成:
heroimage

はじめに

以前からギャラリーは別サーバーの PHP で設置していた。
これを Astro で実現できないかと考えてはいたが Astro 自体よくわかっていなかったの でそれができずにいた。

テスト用ページは以下

ギャラリー
画像一覧ページです
ギャラリー favicon
ギャラリー favicon
https://ubanis.com/oldgallery
ギャラリー
ギャラリー

ギャラリーを作る

readdirSyncがとっても重要だが他のサイトで解説されてるので特に書かない。

ギャラリーの画像の構造

元々のギャラリーは画像のタイプフォルダ以下に年数のフォルダが入った構造になってい る。

    ├── junk
    │   ├── 2016
    │   │   ├── 2016-1211-01.jpg
    │   │   ├── 2016-1211-02.jpg

とりあえずこれに合わせて作ることにした。以下スタイルシートは割愛。

ファイル操作関数

src/utils/fileUtil.ts

import * as fs from "fs";
import * as path from "path";

export function getDirectories(dirPath: string) {
  return fs.readdirSync(dirPath).filter(function (file) {
    return fs.statSync(dirPath + "/" + file).isDirectory();
  });
}

export function getImageFiles(dirPath: string) {
  const imageType = [".gif", ".png", ".jpeg", ".jpg", ".webp", ".avif"];

  const files = fs
    .readdirSync(dirPath, { withFileTypes: true })
    .filter(dirent => dirent.isFile())
    .map(({ name }) => name)
    .filter(function (file) {
      return imageType.includes(path.extname(file).toLowerCase());
    });
  return files;
}

ギャラリーリスト

ギャラリーのリスト用コンポーネント
src/components/GalleryList.astro

---
import { IMAGE_LOCAL_DIR, GALLERY_DIR } from '@config/config';
import { getDirectories } from '@utils/fileUtil.ts';
import Block from './Block.astro';

interface GalleryDirList {
  [key:string]:string[]
};
const galleryList :GalleryDirList ={};
const imageTypeDir = (() => {
try {
  return getDirectories(IMAGE_LOCAL_DIR);
}
catch(e) {
  console.log(e)
  return [];
}
})();

for(const t of imageTypeDir)
{
  galleryList[t] = getDirectories(IMAGE_LOCAL_DIR + t);
}
---

<Block>
{imageTypeDir?.map((type) => (
    <input type="checkbox" name="imagelist" id={type} class="toggle" />
    <label for={type} class="Label">{type}</label>
    <ul class="content">
    {galleryList[type]?.map((year) => {
      return (
        <li><a href=`${GALLERY_DIR}/${type}/${year}`>{year}</a></li>
      );
    })}
    </ul>
))}
</Block>
  • IMAGE_LOCAL_DIRはローカル上でのギャラリー用フォルダへのパス (public/upload/img/)
  • GALLERY_DIR はサイト上のギャラリーページへの URL(/gallery)
  • imageTypeDirにギャラリーフォルダからタイプ別フォルダ一覧の名前を読み込む
  • galleryList[タイプ名]にタイプ名フォルダ以下のフォルダを読み込む
  • タイプ名フォルダの数だけリストを生成する。

ギャラリーのインデックスページ

単純にギャラリーリストが表示されるだけのページ。

---
import BaseLayout from "@layouts/BaseLayout.astro";
import GalleryList from "@components/GalleryList.astro";
---

<BaseLayout pageTitle="ギャラリー" description="画像一覧ページです">
  <div class="parent">
    <div style="align-self:start">
      <GalleryList />
    </div>
    <div>
      <p>メニューから選ぶと、ここに画像一覧が表示されます。</p>
    </div>
  </div>
</BaseLayout>

ギャラリー画像表示ページ

先程のリストを左側に表示し、右側に画像一覧を表示して画像を閲覧するページ
src/pages/gallery/[type]/[year].astro

---
import BaseLayout from "@layouts/BaseLayout.astro";
import GalleryList from "@components/GalleryList.astro";
import MyImage from "@components/MyImage.astro";
import { IMAGE_LOCAL_DIR, IMAGE_WEB_DIR } from "@config/config";
import { getDirectories, getImageFiles } from "@utils/fileUtil";

export function getStaticPaths() {
  interface Params {
    [param: string]: {
      [str: string]: string;
    };
  }
  const paths: Params[] = [];
  const imageTypeDir = (() => {
    try {
      return getDirectories(IMAGE_LOCAL_DIR);
    } catch (e) {
      console.log(e);
      return [];
    }
  })();

  for (const t of imageTypeDir) {
    const years = getDirectories(IMAGE_LOCAL_DIR + t);
    for (const y of years) {
      paths.push({ params: { type: t, year: y } });
    }
  }

  return paths;
}

const { type, year } = Astro.params;
const thumbnailSize = "128px";
const title = type + " " + year;
const imageWebPath = IMAGE_WEB_DIR + type + "/" + year + "/";
const dirLocalPath = "public" + imageWebPath;
const imageSrc: string[] = [];

const imageFiles = (() => {
  try {
    return getImageFiles(dirLocalPath);
  } catch (e) {
    console.log(e);
    return [""];
  }
})();

imageFiles.reverse();
for (const s of imageFiles) {
  imageSrc.push(imageWebPath + s);
}
---

<BaseLayout pageTitle={title} description="画像一覧ページです">
  <link rel="stylesheet" href="/fancybox/fancybox.css" />
  <div class="parent">
    <div style="align-self:start">
      <GalleryList />
    </div>
    <div>
      <h1>{type}</h1>
      <h2>{year}</h2>
      <ul>
        {
          imageSrc?.map((f) => (
            <li>
              <a data-fancybox="group" href={f}>
                <MyImage
                  src={f}
                  width={thumbnailSize}
                  height={thumbnailSize}
                  alt={f}
                />
              </a>
            </li>
          ))
        }
      </ul>
    </div>
  </div>
  <script is:inline src="/fancybox/fancybox.umd.js"></script>
</BaseLayout>
  • IMAGE_WEB_DIRはウェブ上のギャラリー画像用フォルダへの URL(/upload/img/)
  • getStaticPaths でタイプの数だけ年別フォルダのパスを作る(for の二段重ねなので多 分もっと賢いやり方があるはず)
  • タイプ名と年からギャラリー用画像フォルダ以下から拡張子でフィルタリングした画像 のリストを取得する
  • あとはその数だけサムネイルとともにリンクを作成する

最後に

最初律儀に getstaticPaths ですべてのパスの配列を手書きしたり json にしてみたり無 駄なことをしていた。for でreaddirSyncでいいじゃんということに気づいた結果がこ れ。

フォルダツリーすべてをギャラリーにできない上にルートフォルダの画像も表示しない限 定仕様なのでここでしか使えない感じではある。多分正しく賢いやり方があると思うが今 日はここまで。

(ちなみに元ギャラリーの画像は 2000 枚以上あるのでここに置くわけにもいかずここ のギャラリーはブログ用に使うことにします)