どうも、SupabaseでDevRelをしているタイラーです。
意外とちゃんとSupabaseストレージの使い方を日本語で説明している記事がなく、download()
を使ったりした少し回りくどい実装をしているケースを見かけたりしたので、今日はシンプルにSupabaseストレージに画像をアップロードしてその画像を表示するやり方を紹介します。
アップロード
アップロードはシンプルにstorage.upload()
メソッドを呼ぶだけでできます。下記はReactを使った簡単な例です。type="file"
のinput
エレメントを用意して、そのエレメントに対して選択されている画像が変更されたときにhandleImageChange()
メソッドが呼ばれ画像がアップロードされるようになっています。
const handleImageChange = async (
event: ChangeEvent<HTMLInputElement>
): Promise<void> => {
if (!event.target.files || event.target.files.length == 0) {
// 画像が選択されていないのでreturn
return
}
const file = event.target.files[0] // 選択された画像を取得
const filePath = `my_folder/${file.name}` // 画像の保存先のpathを指定
const { error } = await supabase.storage
.from('my_bucket')
.upload(filePath, file)
if (error) {
// ここでエラーハンドリング
}
}
...
<input type="file" onChange={handleImageChange} />
ここではfilePath
という変数でファイルを指定バケットのどこのパスに保存するかを指定する形になっています。filePath
は他にもこのようにユーザーのユーザーIDを使った方法などが主流かもしれません。
const user = await supabase.auth.getUser() // ログイン中のユーザーのユーザーオブジェクトを取得
const filePath = `${user.id}/${file.name}` // ユーザーIDのフォルダの中にファイルを保存
ここではReactとTypescriptを使った例を紹介しましたが、Flutterなど他のフレームワークでも同じようなメソッドがあるのでほぼ同じ感じで実装できます。
画像の表示
画像の表示はとてもシンプルです。Webであろうがモバイルアプリであろうが画像はその画像のURLさえ取得できてしまえば表示できるのでURLを取得しましょう。URLの取得の仕方は2種類あります。
エラーハンドリングは省略しますが、
// publicなバケットに画像が保存されている場合
const { data } = supabase.storage.from('my_bucket').getPublicUrl(filePath)
const imageUrl = data.publicUrl
// privateなバケットに画像が保存されている場合
const {data, error} = await supabase.storage.from('my_bucket').createSignedUrl(filePath, 600)
const imageUrl = data.signedUrl
これだけでimageUrl
変数に画像のURLが格納されます。あとはこれをウェブであれば<img />
のsrc
に指定するだけだし、FlutterであればImage.network
ウィジェットに渡してあげるだけです。
あと、よくあるのは画像をアップロードした際にその画像のURLを取得してしまって、そのURLをデータベースに保存してしまう手法です。個人的に何かアプリを作るときはこのやり方をいつも使っています。
const handleImageChange = async (
event: ChangeEvent<HTMLInputElement>
): Promise<void> => {
if (!event.target.files || event.target.files.length == 0) {
// 画像が選択されていないのでreturn
return
}
const file = event.target.files[0] // 選択された画像を取得
const filePath = `my_folder/${file.name}` // 画像の保存先のpathを指定
const { error } = await supabase.storage
.from('my_bucket')
.upload(filePath, file)
if (error) {
// ここでエラーハンドリング
}
// 画像のURLを取得
const { data } = supabase.storage.from('my_bucket').getPublicUrl(filePath)
const imageUrl = data.publicUrl
// 画像のURLをDBに保存
const { error: databaseError } = await supabase
.from('my_table')
.insert({ imageUrl: imageUrl })
}
DBに保存する部分のコードは実際にはユースケースによって変わってくると思うのですが、例えばこの画像アップロードがプロフィール画像を変更するような処理なのであればprofiles
テーブルに対してupdate
するような処理になるでしょうし、何か画像つきの投稿をするようなアプリなのであればposts
テーブルに対して投稿文などの他のデータと一緒にinsert
するような処理になるのかなと思います。
このようにDBに画像のURLを挿入してあげることで今度そのDBからデータをロードした際にはsupabase.storage
系のメソッドを呼ぶことなく直接そのURlを<img />
などに渡してあげることで表示することができ、実装が楽になります。
以上、少しでもみなさんのSupabaseストレージの理解を深めるお手伝いができたら光栄です。