【Supabase】Storageへ画像をアップロード/400エラーになる原因を調査&解決
Supabase Storageへ画像をアップロードしようとしたところ、400エラーで詰まりました。
解決できたので、その原因と解決方法についてまとめました。
Supabase Storageの設定
下記の記事などを参考に、Supabase Storageへの画像アップロードを試みました。
①publicバケットの作成
以下のとおり、publicバケットを作成しました。
②ポリシーの作成
簡単にポリシーを作成できるので、「Get started quickly」を選択して進みました。
認証済みユーザだけがアクセスできるようにしたかったので、
「Give users access to a folder only to authenticated」を選んでテンプレートを作成しました。
Allowed operationと、Target rolesのところを選択して、次へ進みました。
このまま「Save policy」と進めて、ポリシーが作成できました。
ソースコードの準備(Next.js)
Supabaseを利用するのに必要な記述などは省き、画像アップロードに関する部分だけに絞って記載します。
以下の関数を呼び出すことで、Supabase Storageにアップロードできます。
const uploadImg = async (file: File, fileName: string) => {
try {
const { error } = await supabase.storage
.from("public-img-bucket") // バケットID
.upload(`img/${fileName}`, file); // アップロード先
if (error) {
throw error;
}
} catch (err) {
console.error(err);
}
};
APIについては、以下に記載があります。
input
タグのtype="file"
でファイルを選択できるようにしました。
<input
type="file"
accept="image/*"
ref={fileInputRef}
onChange={handleChangeImg}
/>
handleChangeImg
関数は以下のようにしました。
const handleChangeImg = async ( e: React.ChangeEvent<HTMLInputElement> ) => {
const { files } = e.target;
if (!!files && !!files[0]) {
const fileExtension = files[0].name.split(".").pop();
setImgFile(files[0]);
setImgFileName(`${uuidv4()}.${fileExtension}`);
}
};
imgFile
とimgFileName
がセットされた状態で、uploadImg(imgFile, imgFileName)
を実行すると、Supabase Storageへのアップロードが実行されます。
実行してみると400エラー
上記のソースコードを実行してみると、400エラーが返ってきました。
400エラーの原因調査
エラー内容を見てみると、ポリシーに何か問題がありそうです。
message: 'new row violates row-level security policy'
ポリシーの設定を見直してみます。
バケットのポリシー設定に戻ってみると、各操作ごとに編集できるようになっています。
SELECT操作の編集ページを開いてみました。
USING expressionについて、紐解いてみます。
3つの条件がANDで結ばれています。
(正確には、2つ目と3つ目のANDの結果と1つ目をANDで結んでいますね。)
1つ目。
バケットIDが作成したバケットIDと一致しているかチェックしています。
問題ないと思います。
(bucket_id = 'public-img-bucket'::text)
2つ目。
ストレージのフォルダ名が'private'
であるかをチェックしています。
コード上では、フォルダ名を'img'
にしていたので、ここの条件に外れてしまっていそうです。
(storage.foldername(name))[1] = 'private'::text)
3つ目。
ロールが'authenticated'
であるかをチェックしています。
問題ないと思います。
(auth.role() = 'authenticated'::text)
というわけで、2つ目の条件に問題がありそうかなというところまで見えてきました。
400エラーの解決方法
解決方法は2つあります。
私は、②の方法で解決に至りました。
解決方法①
ソースコードでアップロード先にしているフォルダ名を変更する。
const uploadImg = async (file: File, fileName: string) => {
try {
const { error } = await supabase.storage
.from("public-img-bucket")
.upload(`private/${fileName}`, file); // アップロード先をprivate/に変更
if (error) {
throw error;
}
} catch (err) {
console.error(err);
}
};
解決方法②
Supabase Storageのポリシー設定で、USING expressionの条件を変更する。
以下のように、privateとなっていたところをimgに変更すればOKです。
ご希望のフォルダ名がある場合は、その名前で設定して、ソースコードも同様に変更してください。
((bucket_id = 'public-img-bucket'::text) AND ((storage.foldername(name))[1] = 'img'::text) AND (auth.role() = 'authenticated'::text))
各操作ごとにポリシー設定の編集ページが分かれているので、対象個所すべてに変更が必要です。