Supabaseでの画像管理に挑戦!

Supabaseでの画像管理に挑戦!

投稿日: 2024年12月30日

学習振り返り
要約
  • Reactで独自のtodoアプリを作成中で、Supabaseと連携した画像管理ページを振り返った。
  • 画像をバケットにアップロードし、テーブルにメタデータを登録する仕組みを実装した。
  • UUIDを利用して一意の画像名を生成し、複数のユーザーが同時にアクセスする場合の整合性を確保した。

はじめに

現在、オリジナルアプリ制作の12章におりまして、Reactで独自のtodoアプリを作成中です。先ほどぶべさんからLGTMをいただいた(うれしい😭)画像管理のページを振り返り、学びを記録をします。

今回のブログは、データベース連携をすでに御存知の方には当たり前やん!の内容になるかと思います🙇‍♀️もし読んで、解釈が異なればご指摘ください!
私はまだまだ初学者なので、なるほど✨と思ったところです。ご了承ください🙇‍♀️

作成経緯

私には小学生の子供が2人いるのですが、学校から持って帰ってくるお手紙が毎度たくさん。2人共通のお知らせがあったり、それぞれの学年のプリントがあったりと、とても煩雑で、全然整理できておりません…
それを整理したくて、各タブでカテゴリー分けができて、その中に画像(プリント)を登録・整理していけるようなページを作りました。
デザインは以下の通りです。


Supabaseでの画像管理に挑戦!|ShiftBブログ

このページ作成は私にとっていくつもの山場がありましたが、その中の一つである、「画像をSupabaseのデータベースに登録する方法」を記録します。

画像をSupabaseに登録する仕組み

今回はフロントページメイン(page.tsx)の関数の話です。

例えば、「やることリスト」などの文字の箇条書きの場合、APIを使ってデータを送信すると、そのデータがSupabaseのテーブルに登録されます。

以下は、TodoリストのSupabaseのテーブル構造の例です。

Supabaseでの画像管理に挑戦!|ShiftBブログ


画像の場合、このテーブルに「key」という画像名のような文字列を保存し、さらに「bucket」という場所にそのkeyで紐づけた実際の画像を保存します。つまり、実際に画像が保存されている場所は「bucket(バケット)」です。supabaseではStorage部分に保存されます。

Supabaseでの画像管理に挑戦!|ShiftBブログ

これにより、画像のメタデータはテーブルに、実体はバケットに保存されます!

バケットは初期設定が必要で、私はブログ課題の11章を参考にしながら設定を行いました。
ぶべさん、ちゃんと書いててくださってたのに、当時ちゃんと理解できてなくてごめんね。。
今回は、ログインしたユーザーだけが画像を閲覧できるようにするため、バケットの中に「private」フォルダを作成(初期設定したら自動で作成できる)し、画像がそこに保存されるようにしています。

コード例

以下は画像をアップロード(POST)するための関数です。
①バケットに画像を保存する②APIを使ってテーブルデータを作成 の2種類の処理を行っています。

// 画像を追加
const AddImage = async (event: ChangeEvent<HTMLInputElement>) => {
  try {
    if (!event.target.files || event.target.files.length === 0) {
      alert("画像ファイル(タブ)が選択されていません。");
      throw new Error("No image file selected.");
    }

    const file = event.target.files[0];
    const filePath = `private/${uuidv4()}`;

    // ★①Supabase ストレージ(バケット)に画像をアップロード
    const { data: uploadData, error: uploadError } = await supabase.storage
      .from("item")
      .upload(filePath, file, {
        cacheControl: "3600",
        upsert: false,
      });

    if (uploadError) {
      throw new Error(`Upload error: ${uploadError.message}`);
    }
    setThumbnailImageKey(uploadData.path);

    // ★②Supabaseに画像情報を送信
    const response = await fetch(
      `/api/gallery_group/${selectedTabId}/items`,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: token!,
        },
        body: JSON.stringify({
          galleryGroupId: selectedTabId,
          thumbnailImageKey: uploadData.path,
        }),
      }
    );

    if (response.ok) {
      await fetchGalleryItems(); // 画像のリストを再取得
      toast.success("画像が正常に追加されました。", {
        duration: 2100,
      });
    } else {
      const errorData = await response.json();
      throw new Error(
        `API error: ${errorData.message || "Unknown error occurred"}`
      );
    }
  } catch (error) {
    alert("画像の追加に失敗しました。もう一度お試しください。");
    throw error;
  }
};

私は最初、「//★②Supabaseに画像情報を送信」のみ書いて、「No Buket!」というアラートを何度も発生させてしまいました。11章を読み返し、バケットの初期設定と「// ★①Supabase ストレージ(バケット)に画像をアップロード」のコードを追加することにより、無事画像の管理ができるようになりました。
この要領で、PUT(更新)やDELETE(削除)のコードも実装しました。PUTは、はじめ画像を一旦削除し、新規追加するという、めんどくさい方法で書いてましたが、ぶべさんのご指摘で一発で更新する方法に切り替えることができました。
公式HPにばっちり書いてました😶‍🌫️ありがとうございます🙇‍♀️

uuidを使った一意性の確保

画像アップロードの際に重要なのが「uuid(Universally Unique Identifier)」です。これは、一意のkey(ここでは画像名のようなもの)を生成するための仕組みです。
もしこのuuidを使わなかったら、異なるユーザーが同じファイル名(例:image.png)で画像をアップロードした場合、後からアップロードされたファイルが前のファイルを上書きしてしまう可能性があり、データの不整合や予期せぬエラーを招く💦というので、絶対します!複数のユーザーが同時にアクセスするようなシステムにおいて、非常に重要でだそうです。
これも11章の課題でやったこと!と覚えていたので、設定方法等を見返してすぐできたと思います✨

最後に

このページ、ほんと苦労して作りました。私なりにちゃんと書けたと思います。
よくがんばった自分!と思います(笑)
ぶべさんはじめ、お付き合いいただいた方、本当にありがとうございました。
まだオリアプは完成ではないので、引き続き制作楽しみます!
1年の振り返りブログも書けたらいいのですが、今年は力尽きそうです笑
来年の抱負も考えたのですが、ちゃんと定まっていないかもです😶‍🌫️
でも、これだけは言える!
コードはずっと書き続けようと思います!

シェア!

Threads
icon
tomoe
ShiftB 1期生。2児の母。 負けん気とあきらめない心でオリジナルアプリ制作中♪
Loading...
記事一覧に戻る
Threads
0