エラー対応

【Supabase】Storageへ画像をアップロード/400エラーになる原因を調査&解決

nanoha.creator@gmail.com

Supabase Storageへ画像をアップロードしようとしたところ、400エラーで詰まりました。
解決できたので、その原因と解決方法についてまとめました。

Supabase Storageの設定

下記の記事などを参考に、Supabase Storageへの画像アップロードを試みました。

①publicバケットの作成

以下のとおり、publicバケットを作成しました。

「Create storage bucket」にて、「Public bucket」を有効化してバケットを作成する

②ポリシーの作成

簡単にポリシーを作成できるので、「Get started quickly」を選択して進みました。

「Adding new policy to public-img-bucket」より、「Get started quickly」を選択する

認証済みユーザだけがアクセスできるようにしたかったので、
「Give users access to a folder only to authenticated」を選んでテンプレートを作成しました。

「Give users access to a folder only to authenticated users」を選択して、「Use this template」ボタンをクリックする

Allowed operationと、Target rolesのところを選択して、次へ進みました。

「Allowed operation」「Target roles」を設定して、「Review」ボタンをクリックする

このまま「Save policy」と進めて、ポリシーが作成できました。

ポリシー内容をチェックして「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}`);
  }
};

imgFileimgFileNameがセットされた状態で、uploadImg(imgFile, imgFileName)を実行すると、Supabase Storageへのアップロードが実行されます。

実行してみると400エラー

上記のソースコードを実行してみると、400エラーが返ってきました。

400 (Bad Request)
---
{statusCode: '403', error: 'Unauthorized', message: 'new row violates row-level security policy'}

400エラーの原因調査

エラー内容を見てみると、ポリシーに何か問題がありそうです。

message: 'new row violates row-level security policy'

ポリシーの設定を見直してみます。
バケットのポリシー設定に戻ってみると、各操作ごとに編集できるようになっています。

Supabase管理画面に戻って、ポリシー設定を見直す

SELECT操作の編集ページを開いてみました。

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))

各操作ごとにポリシー設定の編集ページが分かれているので、対象個所すべてに変更が必要です。

参考記事

ABOUT ME
Haruna Abe
Haruna Abe
Webエンジニア
フロントエンドエンジニア2年目|ReactやTypeScriptなど、フロントエンドの情報を発信していきます
CONTACT

お問い合わせ

制作のお見積もりやご依頼、その他ご相談等ございましたら
お気軽にお問い合わせください。

    記事URLをコピーしました