Firebaseバージョン9でStorageを使って画像をアップロードする(React)

2021年8月25日にリリースされた`Firebase JavaScript SDK(ver.9.0.0)`では従来のバージョンのコードとの互換性に問題がありました。
また, 新バージョンでの`Firebase Storage`を扱っている記事があまりないと感じたので,
 本記事ではver.9.0.0のFirebaseを用いてFirebase Storageに画像をアップロードする方法について解説します。
ver.9.0.0より前のコードで実装していた人は是非ご覧ください。

## 初めに
・**macOS Monterey ver.12.0.1**
・**Visual Studio Code ver.1.62.3**
・**TypeScript ver.4.3.5**
・**React ver.17.0.2**
・**Firebase ver.9.0.0**
・**Node.js ver.16.0.0**
・**yarn ver.1.22.15**

※本記事ではあらかじめ`firebase`が使える環境が整っている前提とします。まだ整っていない方は公式のチュートリアルを参考にしてください。  
[FirebaseをJavaScriptプロジェクトに追加する](https://firebase.google.com/docs/web/setup?hl=ja)

## 画像アップロード用ファイルを作成する

初めに, 本記事では画像のアップロードのみが目的なので, 一つのファイルに処理をまとめます。
分けた方が良いという意見もあるとは思いますが多めにみていただけると幸いです。
処理の大まかな流れとしては,  
**参照を作成する** → **ファイルをアップロードする**  
という非常に単純なものです。
細かい部分では, ファイルのバリデーションも行なっています。
また, firebaseConfigなども一つにまとめましたが, 本来は別ファイルに作って
そこからimportするというのが一般的なので
実際のプロジェクトではそれに従うことをお勧めします。  
以下にコード例を記述します。

```TypeScript
import React, { useState } from "react"
import { initializeApp } from "firebase/app";
import { getStorage, ref, uploadBytes } from "firebase/storage";
import { validateImage } from "image-validator";

const firebaseConfig = {
  apiKey: process.env.FIREBASE_API_KEY,
  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECT_ID,
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.FIREBASE_APP_ID,
  measurementId: process.env.FIREBASE_MEASUREMENT_ID
};

const firebaseApp = initializeApp(firebaseConfig);

const storage = getStorage(firebaseApp);

const App = () => {
  const [file, setFile] = useState<File>(null!);

  // ファイルのバリデーション関数
　　 const validateFile = async (file: File) => {
    // 3GBを最大のファイルサイズに設定
    const limitFileSize = 3 * 1024 * 1024;
    if (file.size > limitFileSize) {
      alert("ファイルサイズが大きすぎます。\n3メガバイト以下にしてください。");
      return false;
    }
    const isValidImage = await validateImage(file);
    if (!isValidImage) {
      alert("画像ファイル以外はアップロードできません。");
      return false;
    }
    return true;
  }

  // 画像選択関数
  const handleImageSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
    e.preventDefault();
    const reader = new FileReader();
    const file = e.target.files![0];
    if (!(await validateFile(file))) {
      return;
    }
    reader.onloadend = async () => {
      setFile(file);
    };
    reader.readAsDataURL(file);
  };

  // 画像アップロード関数
  const uploadImage = async () => {
  // 参照を作成 → 'images/(画像名)'
  const storageRef = ref(storage, `images/${file.name}`);
  await uploadBytes(storageRef, file)
      .then((snapshot) => {
        console.log("アップロードに成功しました");
      })
      .catch((error) => {
        console.log("アップロードに失敗しました");
      });
  }

  return (
    <div>
      <form onSubmit={uploadImage}>
        <input type="file" onChange={handleImageSelect} />
        <button type="submit">アップロード</button>
      </form>
    </div>
  )
}

```

ファイルのバリデーションは`image-validator`というライブラリを使用しています。
これによって画像ファイル以外を選択できないようにしています。
今回は3メガバイト以上のファイルを選択できないようにしましたが, 値設定は自由です。

## 終わりに

Firebase公式の日本語版ドキュメントではver9.0.0のFirebase Storage用のチュートリアルが無く, 困惑しました。**英語版**であれば記載しているのでそちらを見ればアップロード以外にも
`ダウンロード`や`削除`の実装なども詳細に知ることができます。

## 参考

https://firebase.google.com/docs/storage/web/start

https://zenn.dev/hiro__dev/articles/605161cd5a7875