【Firebase】Cloud Functionsからstorageへのアクセスについて理解する
Firebase と Google Cloud は少し違うものだ!!!正確に言うとFirebaseはGoogle Cloud プロジェクトに独自のサービスや機能を付与したもので、Firebaseプロジェクトの実態はGoogle Cloud プロジェクトだけども、使うAPI、SDK、メソッドが異なるので、これらをごっちゃにしてる状態でググると訳わからなくなった。ので書いた!
Firebase プロジェクトと Google Cloud の関係
Firebase コンソールで新しい Firebase プロジェクトを作成した場合、内部で実際に作成されるのは Google Cloud プロジェクトです。Google Cloud プロジェクトは、データ、コード、構成、サービスのための仮想的なコンテナと考えることができます。Firebase プロジェクトは、Google Cloud プロジェクトに Firebase 固有の構成とサービスを加えたものです。はじめに Google Cloud プロジェクトを作成し、その後に Firebase を追加することもできます。
https://firebase.google.com/docs/projects/learn-more?hl=ja
各プロダクトの名称の違い
Firebase
- Cloud Firestore
- Cloud Functions for Firebase
- Cloud Storage (for Firebase )
Google Cloud
- Firestore
- Google Cloud Functions
- Google Cloud Storage
基本的には最初に「Cloud〜」と付いたら Firebase の方のプロダクトのことだと覚えておけばいい。繰り返しになるがFirebaseの各プロダクトも中身はGoogle Cloud プロジェクトであり、Firebaseコンソールでアクセス可能なプロジェクトはGCPコンソール画面からもアクセスが可能である。
本題
で今回は Firebase での色んなやり方を書いてます。
初期化の方法
ローカルでテストで実行する場合を除いて、基本的にFirebaseに上げてるCloud FunctionsはinitializeApp()
の引数の指定は不要で初期化が可能。
//index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
//index.ts
import * as functions from 'firebase-functions';
import * as admin from "firebase-admin";
const firebase = admin.initializeApp();
基本的に(Firebaseの)Firestore × Cloud Functions ではfunctions
とadmin
のモジュールを読み込めばOKみたい。別途サービスアカウントを設定したり、admin.config
を設定したりするケースもあるようだが、上記がまず基本形でゆるがない!
Cloud Functions から Cloud Storage にアクセスする方法
あくまでFirebaseでの話。
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
const storage = admin.storage();
// db.settings({ ignoreUndefinedProperties: true }) // undifinedでエラーにならないための設定
// 例)Firestoreのデータが削除されたら Cloud Storageのデータも消す
exports.onDeleteMyPost = functions.firestore
.document('/users/{userId}/posts/{postId}')
.onDelete((snapshot, context) => {
const userId = context.params.userId;
const fileName = snapshot.data().postImageFileName; // 画像アップロード時にFirestoreに書き込んでおいたファイル名
const path = `post/${userId}/images/${fileName}`; // Cloud Storage のパス
return storage.bucket().file(path).delete(); // ←こんな感じでbucketを取得し操作する
})
ここでいうstorage.bucket()
は中身はGoogle Cloud で定義されているBucketと同じものとドキュメントで言われている。なのでdelete()
以外にも、持ってるプロパティや使えるメソッドを知りたいという方は Google Cloud のドキュメントのBucketの項目を見てということらしい。
Cloud Functions ではサービスアカウントの設定は不要
ちなみにFirebaseのCloud Storageのドキュメント「Admin Cloud Storage API の概要」をみると以下のようにサービスアカウントやバケット名を設定している。しかしFirebaseのCloud Functionsのindex.js
もしくはindex.ts
においては明示的に指定しなくても自分が属するプロジェクトのバケット名などを知っているようだ。
var admin = require("firebase-admin");
var serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
storageBucket: "<BUCKET_NAME>.appspot.com"
});
var bucket = admin.storage().bucket();
// 'bucket' is an object defined in the @google-cloud/storage library.
admin.storage と firebase.storage は違う
storage.refFromURL()
を使いたかった
もともとはそもそもやりたかったのは、Cloud Storage のダウンロードURLを Cloud Firestore に保存しているので、これをもとにファイルの保存場所を取得したかった。しかしstorage.ref()
やstorage.refFromURL()
と書けるのはFirebase Javascript SDKのstorageだけなので、Web上の文献で非常に混乱する
JavaScriptで Cloud Storage にアクセスする方法は3つある
-
Firebase API
- Firebase Admin SDK
- Firebase JavaScript SDK
-
Google Cloud Storage API
- Google-Cloud Server SDK
このうち Firebase JavaScript SDK はクライアント側(フロントエンド?)で使うものらしく、Cloud Functions のNode.jsで使えるのは Firebase Admin SDK と Google-Cloud ServeSDK の2つのようだ。
※SDK=Software Development Kit。APIとかを簡単に使えるようにしてくれる奴
Admin SDK からアクセスできるstorage
先の初期化方法を用いてアクセスできるstorageはFirebase Admin SDKを使って取得したものである。このSDKは主にCloud Functionsなどのサーバーサイドで使うものらしい。
https://firebase.google.com/docs/reference/admin/node/admin.storage.Storage-1
Firebaseのドキュメントにおけるadmin.storegeはプロパティapp
とメソッドbukect
しか持ってない。
Javascript SDK からアクセスできるstorage
これはJavascriptを使うクライアント=主にWebのフロントエンド?(React, Vue, Angularとか?)が使うSDKらしい。
https://firebase.google.com/docs/reference/js/firebase.storage.Storage?hl=ja
firebase.storageは.ref()
や.refFromURL()
メソッドを持っている。
Google Cloud Storage API からアクセスできるstorage?
firebase-admin
モジュールやfirebase
モジュールではなく、@google-cloud
のモジュールを読み込んで、Google Cloud Storage としてstoregaを取得する方法もあるらしい‥。もう訳がわからん‥。
//index.js
// Require gcloud
var gcloud = require('google-cloud');
// Enable Storage
var gcs = gcloud.storage({
projectId: 'grape-spaceship-123',
keyFilename: '/path/to/keyfile.json'
});
// Reference an existing bucket.
var bucket = gcs.bucket('my-existing-bucket');
// Upload a local file to a new file to be created in your bucket.
bucket.upload('/photos/zoo/zebra.jpg', function(err, file) {
if (!err) {
// "zebra.jpg" is now in your bucket.
}
});
// Download a file from your bucket.
bucket.file('giraffe.jpg').download({
destination: '/photos/zoo/giraffe.jpg'
}, function(err) {});
https://firebase.google.com/docs/storage/gcp-integration?hl=ja
その他の Google Cloud モジュールを使うケース
他にもググっていると以下のようにfirestoreを定義しているケースがある。これはFirestoreのデータを定期的に自動バックアップするFunctionだが、この場合はadmin.firestore()
ではなくて、@google-cloud/firestore
で読み込んだものを使わないと動かなかった。おそらくデータベースのexportやbucketという概念がFirebaseの中に正式に準備されてないので、(中身はGoogle Cloud プロジェクトなので)Google Cloud Firestore として処理を実行しないといけないためだろう。
//index.js
const firestore = require("@google-cloud/firestore");
const client = new firestore.v1.FirestoreAdminClient();
const bucketForExport = "gs://myproject-export";
exports.scheduledFirestoreExport = functions.pubsub
.schedule("45 19 * * *")
.onRun((context) => {
// 省略
参考
・記述が正解かは不明だが参考になりそうなStackoverflow
・Google Cloud の Cloud Storage でのオブジェクトの削除方法
Discussion