はじめに
こんにちは!かほです♪🐥
現在、本業では技術広報業務やエンジニア業務などに従事しています。
今回の記事では、Next.js×Supabaseを用いたアプリ開発における画像投稿機能の実装について説明します。
Next.js×Supabase×Vercelの連携方法から知りたい方は、下記の記事を参考にしてください🙌
本記事の画像投稿機能では、下記の認証機能が実装済みです。
認証機能をまだ実装していない方は、下記の記事を参考にしてください🙌
この記事の読者対象
- Next.js×Supabaseで画像表示を含むアプリを作ってみたい方
- Supabaseで画像アップロード機能を含むアプリを作ってみたい方
- Supabaseを今後使ってみたい方
- Next.jsを使って高速でアプリを作りたい方
開発環境
"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を設定しよう
バケットを作成しよう
最初に画像を保管する場所であるバケットの設定を行います。
- 左端のバーにある
Storage
のアイコンを押します。 - すると、まだバケットを作成していない場合下記の画面が出てきます。
Create a new bucket
ボタンを押しましょう。
3. Name of bucket
に設定するバケット名を書きます。また、バケットの設定にはPublic
とPrivate
の2パターンが存在します。今回はPublic
で設定します(誰でも写真を閲覧できるようになるので、変な写真はアップロードしないように気をつけましょう笑)。最後にCreate bucket
のボタンを押します。
4. これでバケットの作成は完了です!バケットはPublic
設定にしてある場合に、バケット名の右横に黄色でPublic
と表示されます。(また、設定をPrivate
に変更したい場合は、バケット名に触れると出てくる縦の点表示を押せば可能です。)
5.アップロードする写真をフォルダーごとに分けたいため、右上にあるCreate folder
を押してフォルダーを作成します。今回のフォルダー名はcharactersに設定しました。
RLSを設定しよう
Storageに保存された写真をブラウザ上でアップロードしたり閲覧したりするには、Policyの作成による認可が必要です。RLSを知らない人は下記の記事の「そもそもRLSとはなんぞや?」の章をご参考に🙌
Policyを作成しよう
次にPolicyを作成します。上記で紹介した記事の「Policyを作成しよう」の手順と基本的に同じです。
-
Storage
にあるConfiguration
のPolicies
を選択すると下記の画面が出てきます。今回は、other policies under storage.objects
の列にPolicyを作成します。右端にあるNew Policy
というボタンを押します。
2. ボタンを押すと下記の画面のように選択肢が2つ出てきます。テンプレートを使用してPolicyを作成したい方はGet started quickly
を選択し、完全にゼロからPolicyを作成したい方はFor full customization
を選択します。
3. 今回はテンプレートから作成したいので、Get started quickly
を選択しました。下記のような画面ができます。上から取得
、挿入
、更新
、削除
の機能に対するSQLテンプレートがあります。
.
画像に対する認可を設定しよう
画像の投稿と閲覧に対する認可の設定を行います。
- 画像に対する認可を設定します。今回は画像のアップロードができる人の認可を設定したいので、4つのSQLテンプレートの中から
Enable insert access for authenticated users only
(上から2つ目)を選択し、Use this template
ボタンを押します。すると下記の画面が出てきます。
. -
Policy name
を設定します。 -
Allowed operation
を設定します。選択肢が4つありますが、データを挿入したいのでINSERT
を選択します。 -
Target roles
を設定します。今回はログイン済みのアカウントを対象とした認可のため、選択肢からauthenticated
を選択します。 -
WITH CHECK expression
を設定します。ここに書くもののイメージとしてはSQLでいうWHERE句だと考えていいでしょう。今回は、ログイン済みのアカウントであれば、常に投稿できる状態にしたいのでtrue
と書きます。 - 右下の
Review
のボタンを押します。設定したSQL情報が下記の画面のように出てくるため、情報に間違えがなければSave policy
を押してPolicyを保存します。
- Policyが作成され、ログイン済みのアカウントのみが画像をアップロードできるように設定しました。上記のような方法と同様で、ブラウザで画像を誰でも閲覧できるようにPolicyを作成しましょう。4つのSQLテンプレートの中から
Enable read access to everyone
(上から1つ目)を使用し、設定を行なってください(説明は省略します)。
Policyの作成を確認しよう
画像に対するPolicyの設定が終わりました。
Policyの設定が完了している場合は、下記の画像のように行として表示されるためご確認ください。また、Policyを編集・削除する場合は右端の縦3点のボタンを押すと可能です。
画像アップロード機能を実装しよう
次に、 画像アップロード機能についての説明を行います。
下記が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の引数にはフォルダー以下の設定パス名、保存するファイル数を表した変数、 cacheControl
、upsert
は固定で下記コードの値を設定します。
from.upload()
を使用する方法は、下記のSupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください🙌
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
の画像の表示機能における全体像です。
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に入れるパスの値を状態管理するための記述を行います。
const [ path,setPathName ] = useState<string | undefined>();
次に上記で実装したuploadStorage
とsupabase.tsを呼び出し、画像表示部分の実装を行います。
uploadStorage
の引数には上記で設定したfileListとbucketNameを入れます。
bucketNameにはバケット名を設定しましょう。
const { path } = await uploadStorage({
folder,
bucketName: "pictures",
});
今回はgetPublicUrl()
を使用し、画像を閲覧できるパブリックな画像URLを生成します。
fromの引数には保存先のバケット名、getPublicUrl()
の引数にはuploadStorage
によって設定されたフォルダー以下のパスを設定します。
getPublicUrl()
を使用する方法は、下記のSupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください🙌
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()
を呼び出します。
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で画像保存を確認しよう
上記のようにブラウザで画像表示が確認できたら、Supabase
のStorage
に画像が保存されているか確認しましょう。下記のように画像が保存されています。画像右下にはDownload
とGet URL
ボタンがあります。前者は保存した画像をダウンロードでき、後者は保存した画像URLを取得して、URLを叩くとブラウザで画像が表示できます。
最後に
これで実装はおしまいです!
今回は、Next.js×Supabaseを用いたアプリ開発における画像投稿機能の実装について説明しました。今後は主にフロントエンド周辺、コミュニティ運営、Tech PR(技術広報)の記事を中心に書いていく予定ですので、気になる方はぜひフォローをよろしくお願いします♪ではでは〜🐥
Twitterアカウント:https://twitter.com/kaho_eng
追記
前回と前々回のSupabase画面がDarkだったのですが、今回はLightにしてみました〜٩(ˊᗜˋ*)و
DarkとLightしかまだバリエーションがないけれど、今後もっと増えると嬉しいなぁ♪
参考資料