Procom開発日誌:Firebase画像保存のバグ対応とその解決までの道のり

はじめに

こんにちは。今日は、私が開発しているSNSプラットフォーム「Procom(プロコム)」の中で発生した写真アップロード機能に関するバグについて、対応の記録をブログ形式でまとめてみます。

今回のトラブルは、Firebase Storageへの画像保存処理とJavaScript側の連携ミスによるもので、最終的に無事解決できたので、同じような問題で悩む開発者の参考になればと思い記録しておきます。

発端:写真保存ボタンが効かない

最初に気づいたのは、写真アップロード機能を備えた編集画面で「📸 写真保存」ボタンをクリックしても、何の反応も起きないという現象でした。ボタンはクリックできるのに、サーバーへのリクエストが飛んでいないようで、何も保存されない。

まず、ブラウザのデベロッパーツール(Console)を確認すると、下記のようなエラーが出ていました:

Uncaught ReferenceError: files is not defined

これはJavaScriptのスコープの問題で、files 変数が定義されていない箇所で呼び出されていたためです。これを解消することで第一関門はクリア。

第二関門:QuotaExceededErrorの出現

次に出てきたのが以下のエラー:

QuotaExceededError: Failed to execute 'setItem' on 'Storage': Setting the value of 'photos' exceeded the quota.

このエラーは、ブラウザのlocalStorageに画像(base64形式)を直接保存しようとしたため、容量制限(通常5MB)を超えてしまったことが原因です。画像の保存場所としてlocalStorageを使うのは最適ではないと判断し、代わりにFirebase Storageに保存するようアーキテクチャを変更することにしました。

第三関門:404エラー /api/uploadPhotos が存在しない

JavaScript側でbase64画像をFirebaseにアップロードするために、/api/uploadPhotos というAPIエンドポイントを叩いていましたが、サーバー側にこのエンドポイントが存在していないことが発覚。

この時点でサーバーコード(server.js)を確認したところ、画像アップロード処理が未実装だったため、新たに以下のようなAPIを追加する必要がありました:

app.post('/api/uploadPhotos', async (req, res) => {
  const username = req.session.username;
  if (!username) return res.status(401).send('未ログインです');

  const { base64Images } = req.body;
  if (!Array.isArray(base64Images) || base64Images.length === 0) {
    return res.status(400).send('画像データが不正です');
  }

  try {
    const urls = [];

    for (const base64 of base64Images) {
      const base64Data = base64.split(',')[1];
      const buffer = Buffer.from(base64Data, 'base64');
      const filename = `photos/${username}_${uuidv4()}.jpg`;
      const file = bucket.file(filename);

      const token = uuidv4();
      await file.save(buffer, {
        metadata: {
          contentType: 'image/jpeg',
          metadata: {
            firebaseStorageDownloadTokens: token
          }
        }
      });

      const publicUrl = `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o/${encodeURIComponent(filename)}?alt=media&token=${token}`;
      urls.push(publicUrl);
    }

    res.json({ urls });
  } catch (err) {
    console.error('❌ Firebase Storageアップロードエラー:', err);
    res.status(500).send('アップロード失敗');
  }
});

第四関門:Firebase Storage 初期化ミス

次に問題となったのは、Firebaseの初期化部分。最初はadmin.storage()if (!admin.apps.length)の中で呼び出していたため、外のスコープではstorageが未定義になっていました。

この問題を解消するため、admin.initializeApp()の後にグローバルでstoragebucketを定義し直しました:

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

また、bucketNameも正しく取得するために:

const bucketName = bucket.name;

と修正しました。

第五関門:画像URLがFirestoreに保存されない

最後に、画像はFirebase Storageにアップロードされても、そのURLがユーザーのプロフィール情報としてFirestoreに保存されないというバグが発生。

これは、保存処理がphotosフィールドを無視していたためです。これを解消するため、saveProfileAndEventsToServer(true, urls)のように明示的に画像URLを渡し、Firestoreに保存するロジックを見直しました。

また、プロフィール保存時にはsanitizeProfile()関数を用いて、空の配列やundefinedなど不要なデータがFirestoreに送信されないようにしました。

結果:安定した写真アップロード機能の完成

以上の修正により、Procomにおける写真アップロード機能は以下のように動作するようになりました:

  1. 写真を選択し、最大5枚をbase64形式に変換
  2. Firebase Storageにアップロード
  3. トークン付き公開URLを取得
  4. Firestoreにプロフィール情報と一緒に保存
  5. 保存完了後、即座にスライドショーへ反映

まとめ:バグ対応から得た教訓

今回の一連のバグ対応から得た教訓は以下のとおりです:

  • localStorageは画像保存には向いていない
  • Firebase Storageの初期化はスコープに注意
  • 未定義のAPIエンドポイントは必ず404を確認
  • Firestoreに保存するデータは明示的に整形してから渡す
  • JavaScriptとサーバー側の連携ロジックは常に整合性をチェックする

Procomはまだまだ開発途中ですが、今回の修正により、ユーザーが自分の写真を安全かつ快適に保存・表示できるようになりました。

今後も新機能の追加とバグ修正を続けながら、より使いやすいSNSプラットフォームを目指していきます。

おすすめの記事