33
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

話題のSupabaseでサクッと画像投稿機能をつくってみた!

Last updated at Posted at 2023-01-27

はじめに

こんにちは!かほです♪🐥
現在、本業では技術広報業務やエンジニア業務などに従事しています。
今回の記事では、Next.js×Supabaseを用いたアプリ開発における画像投稿機能の実装について説明します。

Next.js×Supabase×Vercelの連携方法から知りたい方は、下記の記事を参考にしてください🙌

本記事の画像投稿機能では、下記の認証機能が実装済みです。
認証機能をまだ実装していない方は、下記の記事を参考にしてください🙌

この記事の読者対象

  • Next.js×Supabaseで画像表示を含むアプリを作ってみたい方
  • Supabaseで画像アップロード機能を含むアプリを作ってみたい方
  • Supabaseを今後使ってみたい方
  • Next.jsを使って高速でアプリを作りたい方

開発環境

package.json
"dependencies": {
    "@supabase/supabase-js": "^2.2.1",
    "eslint": "8.28.0",
    "eslint-config-next": "13.0.6",
    "next": "13.0.6",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "uuid": "^9.0.0"
  },
  "devDependencies": {
    "@types/node": "^18.11.13",
    "@types/react": "18.0.26"
  }

Storageを設定しよう

バケットを作成しよう

最初に画像を保管する場所であるバケットの設定を行います。

  1. 左端のバーにあるStorageのアイコンを押します。
  2. すると、まだバケットを作成していない場合下記の画面が出てきます。
    Create a new bucketボタンを押しましょう。

3. Name of bucketに設定するバケット名を書きます。また、バケットの設定にはPublicPrivateの2パターンが存在します。今回はPublicで設定します(誰でも写真を閲覧できるようになるので、変な写真はアップロードしないように気をつけましょう笑)。最後にCreate bucketのボタンを押します。

4. これでバケットの作成は完了です!バケットはPublic設定にしてある場合に、バケット名の右横に黄色でPublicと表示されます。(また、設定をPrivateに変更したい場合は、バケット名に触れると出てくる縦の点表示を押せば可能です。)

5.アップロードする写真をフォルダーごとに分けたいため、右上にあるCreate folderを押してフォルダーを作成します。今回のフォルダー名はcharactersに設定しました。

RLSを設定しよう

Storageに保存された写真をブラウザ上でアップロードしたり閲覧したりするには、Policyの作成による認可が必要です。RLSを知らない人は下記の記事の「そもそもRLSとはなんぞや?」の章をご参考に🙌

Policyを作成しよう

次にPolicyを作成します。上記で紹介した記事の「Policyを作成しよう」の手順と基本的に同じです。

  1. StorageにあるConfigurationPoliciesを選択すると下記の画面が出てきます。今回は、other policies under storage.objectsの列にPolicyを作成します。右端にあるNew Policyというボタンを押します。

2. ボタンを押すと下記の画面のように選択肢が2つ出てきます。テンプレートを使用してPolicyを作成したい方はGet started quicklyを選択し、完全にゼロからPolicyを作成したい方はFor full customizationを選択します。

3. 今回はテンプレートから作成したいので、Get started quicklyを選択しました。下記のような画面ができます。上から取得挿入更新削除の機能に対するSQLテンプレートがあります。
.

画像に対する認可を設定しよう

画像の投稿と閲覧に対する認可の設定を行います。

  1. 画像に対する認可を設定します。今回は画像のアップロードができる人の認可を設定したいので、4つのSQLテンプレートの中からEnable insert access for authenticated users only(上から2つ目)を選択し、Use this templateボタンを押します。すると下記の画面が出てきます。
    .
  2. Policy nameを設定します。
  3. Allowed operationを設定します。選択肢が4つありますが、データを挿入したいのでINSERTを選択します。
  4. Target rolesを設定します。今回はログイン済みのアカウントを対象とした認可のため、選択肢からauthenticatedを選択します。
  5. WITH CHECK expressionを設定します。ここに書くもののイメージとしてはSQLでいうWHERE句だと考えていいでしょう。今回は、ログイン済みのアカウントであれば、常に投稿できる状態にしたいのでtrueと書きます。
  6. 右下のReviewのボタンを押します。設定したSQL情報が下記の画面のように出てくるため、情報に間違えがなければSave policyを押してPolicyを保存します。
    スクリーンショット 2023-01-27 16.38.59.png
  7. Policyが作成され、ログイン済みのアカウントのみが画像をアップロードできるように設定しました。上記のような方法と同様で、ブラウザで画像を誰でも閲覧できるようにPolicyを作成しましょう。4つのSQLテンプレートの中からEnable read access to everyone(上から1つ目)を使用し、設定を行なってください(説明は省略します)。

Policyの作成を確認しよう

画像に対するPolicyの設定が終わりました。
Policyの設定が完了している場合は、下記の画像のように行として表示されるためご確認ください。また、Policyを編集・削除する場合は右端の縦3点のボタンを押すと可能です。
スクリーンショット 2023-01-27 16.46.47.png

画像アップロード機能を実装しよう

次に、 画像アップロード機能についての説明を行います。
下記がstorage.tsxの画像アップロード機能における全体像です。

storage.tsx
import { v4 as uuidv4 } from "uuid";
import { supabase } from "../utils/supabase";

type UploadStorage = {
  folder: FolderList;
  bucketName: string;
};

type UploadPathname = {
  path: string;
};

export const uploadStorage = async ({
  folder,
  bucketName,
}: UploadStorage): Promise<UploadPathname> => {
    const file = folder[0]; // 1ファイルアップロード
    const pathName = `characters/${uuidv4()}`; // パス名の設定
    const { data, error } = await supabase.storage
      .from(bucketName)
      .upload(pathName, file, {
        cacheControl: "3600",
        upsert: false,
      });
    if (error) throw error;
    return {
      path: data?.path ?? null,
    };
};


主要な部分のコードを説明します。
supabase.tsを呼び出し、画像のアップロード部分の実装を行います。
今回はfrom.upload()を使用し、Storageのバケットに画像をアップロードします。

fromの引数には保存先のバケット名、uploadの引数にはフォルダー以下の設定パス名、保存するファイル数を表した変数、 cacheControlupsertは固定で下記コードの値を設定します。

from.upload()を使用する方法は、下記のSupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください🙌

storage.tsx
 export const uploadStorage = async ({
  folder,
  bucketName,
}: UploadStorage): Promise<UploadPathname> => {
    const file = folder[0]; 
    const pathName = `characters/${uuidv4()}`;
    const { data, error } = await supabase.storage
      .from(bucketName)
      .upload(pathName, file, {
        cacheControl: "3600",
        upsert: false,
      });
    if (error) throw error;
    return {
      path: data?.path ?? null,
    };
};

画像表示機能を実装しよう

次に画像表示機能についての説明を行います。
下記がtop.tsxの画像の表示機能における全体像です。

top.tsx
import { useState } from 'react';
import { uploadStorage } from './storage';
import { supabase } from "../utils/supabase";

export default function Example(){
  const [ path,setPathName ] = useState<string | undefined>();
  const handleUploadStorage = async (folder: FolderList | null) => {
    if (!folder || !folder.length) return;
    const { path } = await uploadStorage({
      folder,
      bucketName: "pictures",
    });
    const { data } = supabase.storage.from("pictures").getPublicUrl(path)
    if (path) setPathName(data.publicUrl);
    console.log(path)
  };
  return (
    <label htmlFor="file-upload">
      <span>アップロードする</span>
      <input
        id="file-upload"
        name="file-upload"
        type="file"
        className="sr-only"
				accept="image/png, image/jpeg"
        onChange={(e) => {
          const fileList = e.target?.files;
          console.log(fileList);
          handleUploadStorage(fileList);
        }}
      />
      <img src={path} alt="" width="800" height="500"/>;
    </label>
  );
};

画像表示機能の実装方法について、コードを上から順に説明します。
最初に、画像表示を行う際に使用するimgタグのsrcに入れるパスの値を状態管理するための記述を行います。

top.tsx
  const [ path,setPathName ] = useState<string | undefined>();

次に上記で実装したuploadStorageとsupabase.tsを呼び出し、画像表示部分の実装を行います。
uploadStorageの引数には上記で設定したfileListとbucketNameを入れます。
bucketNameにはバケット名を設定しましょう。

top.tsx
    const { path } = await uploadStorage({
      folder,
      bucketName: "pictures",
    });

今回はgetPublicUrl()を使用し、画像を閲覧できるパブリックな画像URLを生成します。
fromの引数には保存先のバケット名、getPublicUrl()の引数にはuploadStorageによって設定されたフォルダー以下のパスを設定します。

getPublicUrl()を使用する方法は、下記のSupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください🙌

top.tsx
const handleUploadStorage = async (folder: FolderList | null) => {
    if (!folder || !FolderList.length) return;
    const { path } = await uploadStorage({
      folder,
      bucketName: "pictures",
    });
    const { data } = supabase.storage.from("pictures").getPublicUrl(path)
    if (path) setPathName(data.publicUrl);
  };

画像表示機能に関わる全体のマークアップを下記のように行います。
画像アップロード時に、画像名の表示切り替えのために、onChangeイベントによって、handleUploadStorage()を呼び出します。

top.tsx
 return (
    <label htmlFor="file-upload">
      <span>アップロードする</span>
      <input
        id="file-upload"
        name="file-upload"
        type="file"
        className="sr-only"
				accept="image/png, image/jpeg"
        onChange={(e) => {
          const fileList = e.target?.files;
          console.log(fileList);
          handleUploadStorage(fileList);
        }}
      />
      <img src={path} alt="" width="800" height="500"/>;
    </label>
  );

下記がマークアップした一覧画面の画像です。

Storageで画像保存を確認しよう

上記のようにブラウザで画像表示が確認できたら、SupabaseStorageに画像が保存されているか確認しましょう。下記のように画像が保存されています。画像右下にはDownloadGet URLボタンがあります。前者は保存した画像をダウンロードでき、後者は保存した画像URLを取得して、URLを叩くとブラウザで画像が表示できます。
スクリーンショット 2023-01-27 21.12.21.png

最後に

これで実装はおしまいです!

今回は、Next.js×Supabaseを用いたアプリ開発における画像投稿機能の実装について説明しました。今後は主にフロントエンド周辺、コミュニティ運営、Tech PR(技術広報)の記事を中心に書いていく予定ですので、気になる方はぜひフォローをよろしくお願いします♪ではでは〜🐥

Twitterアカウント:https://twitter.com/kaho_eng

追記

前回と前々回のSupabase画面がDarkだったのですが、今回はLightにしてみました〜٩(ˊᗜˋ*)و
DarkLightしかまだバリエーションがないけれど、今後もっと増えると嬉しいなぁ♪

参考資料

33
23
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?