Cloud Storage for Firebaseで複数のバケットを扱う
Cloud Storage for FirebaseおよびFirebase Emulatorで複数バケットを使う際のメモ。
複数バケットの使い分け方法
- 新規バケットを作成する
- サーバーサイドでは
const bucket = getStorage().bucket('new-bucket');
と書いてバケットを指定する - クライアントサイドでは
const storage = getStorage();
と書くとデフォルトのストレージを使用し、const storage = getStorage(firebaseApp, "gs://new-bucket");
と書くとバケットを指定できる
エミュレータ使用時に発生する問題
ローカルではエミュレータをつかって適当な架空のバケット名を指定して動作確認をすると思いますが、
- サーバーサイドでは指定したバケットに対してファイルの読み書きができる
- エミュレータ上にも指定したバケットが表示されて、ファイルが配置される
- クライアントからは、エミュレータ上のデフォルト以外のバケットにアクセスできない
という挙動になります。
これはエミュレータの問題というよりもクライアントSDK側の問題で、バケットを指定する形でリクエストをするとエミュレータを参照せずに実際のCloud Storageに対してバケットの存在確認をしにいってしまうみたいです。
ずっと前からイシューとして挙がっていますが何年も放置されているため、今後もこのままなんじゃないかと思って諦めてます。
なので、エミュレータを使う環境ではクライアントもサーバーも
isLocal ? getStorage() : getStorage(firebaseApp, "gs://new-bucket")
みたいなことをして、デフォルトバケットを使うように統一するしかなさそう。
storage.rulesの管理方法
bucketごとのruleはそれぞれファイルを作成し、firebase.jsonに以下のように記述することでbucketとruleを紐づけることができます。
{
"storage": [
{
"bucket": "xxxxxxx.appspot.com",
"rules": "defaultBucket.storage.rules"
},
{
"bucket": "xxxxxxx-new-bucket",
"rules": "newBucket.storage.rules"
}
]
}
ruleファイルを1つにしてbucket名ごとに分岐を記述するという手もありますが、バケットという上位概念をパスよりも下で分岐させるのが見づらいので個人的にはおすすめしません。
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
// 「-public」を含むバケットは誰でもread可能
allow read: if bucket.matches('.*-public');
// それ以外は認証済みユーザーのみwrite許可
allow write: if request.auth != null;
}
}
}
以下のようにバケット名をベタ書きするパターンはありかもです。
rules_version = '2';
service firebase.storage {
match /b/xxxxxxx.appspot.com/o {
match /{allPaths=**} {
allow read: if true;
allow write: if false;
}
}
match /b/xxxxxxx-public/o {
match /{allPaths=**} {
allow read: if true;
allow write: if false;
}
}
}
環境ごとにstorage.rulesを変えるには
環境ごとにruleを変えたい場合はbucket×環境ごとにruleファイルを作成し、firebase.jsonもfirebase.development.json、firebase.staging.jsonといったように環境ごとに作成するのがおすすめです。
deployをする際には firebase deploy --only storage --config firebase.development.json
といったように、どのfirebase.jsonを適用するか指定することが可能です。
Discussion