Node.jsを使ってFirebase StorageをEmulatorで動かそうとしたら困った話

hakase
hakase

突然だが、私は雰囲気でFlutterやFirebaseを触っている

そんな私が先日かなり困ってしまった事について説明する

ソースコード内の<YOUR_KEY_FILENAME_PATH>や<PROJECT_ID>等は適宜、環境に合わせて書き換えてください

neko
neko

よろしくお願いします

hakase
hakase

csvファイルをFirebase storageにアップロードするスクリプトを

Node.jsで書いていた

neko
neko

そうなんですね

hakase
hakase

次の図の通り、一つのスクリプトでEmulatorと本番環境の両方へアップロードできるかを検証していた。今回詰まったポイントは下のStorage Emulatorに送信する部分だ

一つのスクリプトでEmulatorと本番環境の両方へアップロードできるようなスクリプトの図
neko
neko

できそうですね

hakase
hakase

Storage Emulatorのインストールや起動は簡単だった

公式のドキュメントを参考にして欲しい

neko
neko

簡単そうですね

hakase
hakase

問題は読むドキュメントにあった

今回実装したかった機能は主に管理用の機能だ。

しかし気がつけばクライアント向けのドキュメントを見ており非常に混乱してしまった

neko
neko

気をつけないといけないですね

hakase
hakase

備忘録として、管理者用の機能を実装する為のドキュメントと

クライアント用の機能を実装する為のドキュメントを紹介したいと思う

neko
neko

ありがとうございます

hakase
hakase

管理者用の機能を実装する時に参考になったドキュメントはちら

neko
neko

ありがとうございます

hakase
hakase

管理者として、ファイルをアップロードする為のサンプルコードはこちら

const { initializeApp, cert } = require("firebase-admin/app");

const { getStorage } = require("firebase-admin/storage");

const KEY_FILENAME_PATH = "<KEY_FILENAME_PATH>";
const BUCKET_NAME = "<BUCKET_NAME>";
const LOCAL_FILE = "./storage/test.csv";

const serviceAccount = require(KEY_FILENAME_PATH);

process.env["FIREBASE_STORAGE_EMULATOR_HOST"] = "localhost:9199";

initializeApp({
  credential: cert(serviceAccount),
  storageBucket: BUCKET_NAME,
});

const bucket = getStorage().bucket();

bucket.upload(LOCAL_FILE, {}, async function (err, callback) {
  if (err) {
    console.log(err);
  } else {
    console.log(`uploaded!!!`);
  }
});
neko
neko

ありがとうございます

hakase
hakase

11行目の処理があればEmulatorに繋がるし、無ければ本番へ繋がるぞ

neko
neko

お疲れ様でした

hakase
hakase

クライアントとして、ファイルをアップロードする為のサンプルコードはこちら

const { initializeApp } = require("firebase/app");
const {
  getStorage,
  connectStorageEmulator,
  ref,
  uploadBytes,
} = require("firebase/storage");
const { readFileSync } = require("fs");

const API_KEY_STR = "<API_KEY_STR>";
const PROJECT_ID = "<PROJECT_ID>";
const LOCAL_FILE = "./storage/test.csv";

const firebaseConfig = {
  apiKey: API_KEY_STR,
  authDomain: PROJECT_ID + ".firebaseapp.com",
  databaseURL: "https://asia-northeast1.firebaseio.com",
  projectId: PROJECT_ID,
  storageBucket: PROJECT_ID + ".appspot.com",
};

const app = initializeApp(firebaseConfig);
const storage = getStorage(app);
connectStorageEmulator(storage, "localhost", 9199);

const storageRef = ref(storage, "test.csv");

const metadata = {
  contentType: "text/csv",
};

const localFile = readFileSync(LOCAL_FILE);
uploadBytes(storageRef, localFile, metadata);
neko
neko

ありがとうございます

hakase
hakase

24行目の処理があればEmulatorに繋がるし、無ければ本番へ繋がるぞ

firebaseConfigの設定については公式のドキュメントも分かりやすい

neko
neko

すばらしいですね

hakase
hakase

このようなエラーが出ると思うが

その時はstorage.rulesを更新するか認証用の機能を実装しよう

[StorageError [FirebaseError]: Firebase Storage: User does not have permission to access 'test.csv'. (storage/unauthorized)] {
  code: 'storage/unauthorized',
  customData: { serverResponse: '' },
  _baseMessage: "Firebase Storage: User does not have permission to access 'test.csv'. (storage/unauthorized)"
}
neko
neko

認証関係のエラーですね

hakase
hakase

Emulatorで使ってみたいだけなら、このよう書き換えても良いだろう

ただし、本番へデプロイしないよう注意すること

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if true;
    }
  }
}
neko
neko

デプロイすると危険ですね

hakase
hakase

この記事を書くのにも4時間程かかったように思う

neko
neko

お疲れ様でした

タイトルとURLをコピーしました