Counts the Clouds

Node.jsでFirebase FirestoreのドキュメントをFirebase StorageにJSONでアップロードする
2021.10.13

Firebase
Firebase Firestore
Firebase Storage
Firebase Admin SDK
Node.js

kyle-ryan-pSyfecRCBQA-unsplash.jpg

プロジェクトの準備

Firebase FirestoreのデータをJSONとしてFirebase Storageに保存したい。

まずは、FirebaseコンソールからFirebaseプロジェクトを作成して、Firestoreを有効にする。今回はAdmin SDKしか使わないので、Storageのルールは本番環境にした。

サービスアカウントの取得

Firebase Admin SDKを使うためにサービスアカウントが必要なので取得する。

「プロジェクトの設定」→「サービスアカウント」→「Firebase Admin SDK」→「新しい秘密鍵の生成」→「キーを生成」で、自動的にローカルに資格情報がダウンロードされる。

tmpディレクトリを作成してディレクトリごと.gitignoreに追加。ダウンロードした資格情報を移動してservice-account-file.jsonとする。

.envファイルを作成して.gitignoreに追加。ここに資格情報へのパスを記述しておいて、環境変数として読み込むようにする。

GOOGLE_APPLICATION_CREDENTIALS="./tmp/service-account-file.json"

開発環境の構築

Node.jsからはFirebase Admin SDKで操作する。クライアント用のFirebase SDKではない。

% node -v
v12.22.0
% mkdir firebase_firestore_to_storage
% cd firebase_firestore_to_storage
% yarn init -y
% yarn add dotenv firebase-admin

.gitignoreにnode_modulesも追加。

Firestoreになんでもいいのでデータをアップロードする。Firestoreのスタートガイドを実行するとちょうどよい。Node.jsなので、「Cloud Firestore を初期化する」→「各自のサーバーで初期化する」の部分を参考にする。

require('dotenv').config()
const path = require('path')
const admin = require('firebase-admin')
const serviceAccount = require(path.resolve(
  __dirname,
  process.env.GOOGLE_APPLICATION_CREDENTIALS,
))

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
})

const db = admin.firestore()

async function main() {
  const docRef = db.collection('users').doc('alovelace')
  await docRef.set({
    first: 'Ada',
    last: 'Lovelace',
    born: 1815,
  })
}

main()

実行すると、Firestoreにusersコレクションが作成され、指定したドキュメントが追加された。

% node ./index.js

保存したドキュメントの取得も簡単。

async function main() {
  const docRef = db.collection('users').doc('alovelace')
  const snapshot = await docRef.get()
  console.log(snapshot.data())
}

Storageの動作確認

まずは公式「Admin Cloud Storage API の概要」を参考に。

.envにバケットのフォルダパスを追加。「Firebaseコンソール」→「Storage」でテーブルの左上にある鎖アイコンをクリックしてフォルダパスを取得できる。

FIREBASE_STORAGE_BUCKET="<BUCKET_NAME>.appspot.com"

Admin SDKの初期化時にstorageBucketを指定する。

  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
+   storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
  })

ここからStorageの処理を追加していく。

+   const bucket = admin.storage().bucket()

Admin Cloud Storage API の概要」ではここまでしか教えてくれない。あとはGoogle Cloud Storageと同じだからそっちをみてね、ということらしい。

確認のため画像をアップロード

Google Cloud Storage: Node.js Clientのドキュメントにいろいろサンプルがあるので、まずは画像で試してみる。

Firebaseのロゴをダウンロードしてtmpに入れる。

以下のように書くとアップロードできた。

async function main() {
  const bucket = admin.storage().bucket()
  bucket.upload(path.resolve(__dirname, './tmp/Firebase_Logo_Standard_Lockup.png'), (err, file, apiResponse) => {
    if (err) {
      console.error(err)
      return
    }
    console.log(file)
    console.log(apiResponse)
  })
}

File.saveを使ってアップロード

端末上のファイルではなく、メモリ上のデータを保存する場合は、File.saveを使うらしい。

サンプルを試してみる。

async function main() {
  const bucket = admin.storage().bucket()
  const file = bucket.file('my-file')
  const contents = 'This is the contents of the file.'
  await file.save(contents)
}

保存できたので、逆に保存したファイルをダウンロードしてみる。

async function main() {
  const bucket = admin.storage().bucket()
  const file = bucket.file('my-file')
  await file.download({ destination: path.resolve(__dirname, './tmp/file') })
}

これで、材料はそろったと思う。

FirestoreからgetしたドキュメントをJSONとしてStorageに保存する

これが今回やりたいこと。

とりあえず順当に、取得したデータをJSON.stringifyで文字列にして保存してみる。

async function main() {
  const docRef = db.collection('users').doc('alovelace')
  const snapshot = await docRef.get()
  const data = snapshot.data()

  const bucket = admin.storage().bucket()
  const file = bucket.file(snapshot.id)
  const contents = JSON.stringify(data)

  await file.save(contents)
  await file.download({ destination: path.resolve(__dirname, `./tmp/${snapshot.id}`) })
}

Node.jsでFirestoreのドキュメントをStorageにJSONでアップロードすることができた。