第6回 2020年版 React+Firebaseで画像のアップロード（その１）

# 1. 概要
ReactでFirebaseを利用して、画像ファイルをアップロードする方法について説明します。
FirebaseのCloud Storageに画像をアップロード可能とし、その後、Cloud Functionsでリサイズするアプリを開発するまでの手順を３回に分けて説明します。

# 2. 前提条件
## 作業日時
- 2020/3/18

## 環境
- MacBook Pro (15-inch, 2018)
- macOS Catalina 10.15.2（19C57）


## ソフトウェアのバージョン

| 分類 | ソフトウェア | バージョン |
|:-:|:-:|:-:|
|フレームワーク   | [React](https://ja.reactjs.org/)              | 16.13.0 |
|フレームワーク   | React-dom                                     | 16.13.0 |
|静的型付け     | [TypeScript](https://www.typescriptlang.org/) | 3.7.5   |
|ライブラリ      | [create-react-app](https://github.com/facebook/create-react-app/)  | v3.4.0  |
|ライブラリ      | [Material UI](https://material-ui.com/)        | v4.9.4  |
|             | @material-ui/core  | 4.9.4  |
|             | @material-ui/icons | 4.9.1   |
|Firebase CLI | firebase-tools     | 7.14.0       |

# 3. Firebaseのプロジェクト作成
## 3.1. プロジェクトの作成
まずは、 [FirebaseのWebサイト](https://console.firebase.google.com/u/0/) でプロジェクトを作ります。


1. Firebaseにサインアップします。Googleアカウントがあればすぐ登録できます。
2. プロジェクトを新規作成します。

<img width="1677" alt="Firebase画面.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293373/0c6a6a0f-fa1e-bdae-adf3-a9042f0ffe03.png">


## 3.2. リージョンを選択する。

なるべく多くの機能がある方が良いので、東京リージョン(`asia-northeast1`)を選択する。
*リージョンは後から変更することができないので、注意。
なお、Realtime Databaseのイベントトリガーを利用する場合の推奨リージョンは`us-central1`となる。

- 参考:[Cloud Functions のロケーション](https://firebase.google.com/docs/functions/locations?hl=ja#backgroundfunctions)

## 3.3. Webアプリを追加する
「プロジェクトの概要」　＞　「全般」　＞　「アプリを追加する」でWebアプリを追加します。

## 3.4 Database
Databaseがネイティブモードになっていない場合、移行の初期化時にエラーとなるので、ネイティブモードに変更する。
FirebaseのコンソールからGoogle cloud platformに遷移し、ネイティブモードに変更する。

# 4. Firebase CLIのセットアップ

次に、ローカル環境にFirebase CLIをインストールします。

```console
$ yarn global add firebase-tools
```

Firebase CLIでFirebaseへログインすることで、認証されます。
ブラウザが起動して、ログイン画面が表示されるのでログインします。

```console
firebase login
```

# 5. Reactアプリの作成

Firebaseのサンプル用のReactのアプリを作成します。

```console
$ npx create-react-app firebase-storage-sample --template typescript
$ cd firebase-sample/
```

# 6. Firebaseの初期設定
## 6.1. Firebaseの初期設定を行う

Firebaseの初期化を行います。

```console
$ firebase init

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/hoge/Documents/00_mygit/hoshimado/firebase-sample

```

色々質問をされるので、次のように選択します。

```console
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i>
 to invert selection)
❯◉ Database: Deploy Firebase Realtime Database Rules
 ◉ Firestore: Deploy rules and create indexes for Firestore
 ◉ Functions: Configure and deploy Cloud Functions
 ◉ Hosting: Configure and deploy Firebase Hosting sites
 ◉ Storage: Deploy Cloud Storage security rules
 ◉ Emulators: Set up local emulators for Firebase features

```

→`a`で全て選択します。

```console
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

? Please select an option: Use an existing project
? Select a default Firebase project for this directory: 
❯ sample-9f36d (sample) 
```

→`Use an existing project`を選択肢、その後、先程作成したプロジェクトを選択する。

### Database Setup

```console
Firebase Realtime Database Rules allow you to define how your data should be
structured and when your data can be read from and written to.

? What file should be used for Database Rules? (database.rules.json) 
```

→`database.rules.json`を利用するので、そのままエンター。

### Firestore Setup

```console
Firestore Security Rules allow you to define how and when to allow
requests. You can keep these rules in your project directory
and publish them with firebase deploy.

? What file should be used for Firestore Rules? (firestore.rules) 
```

→`firestore.rules`を利用するので、そのままエンター。

```console
? What file should be used for Firestore indexes? (firestore.indexes.json) 
```

→`firestore.indexes.json`を利用するので、そのままエンター。
なお、Databaseがネイティブモードになっていない場合、エラーとなる。
＊冒頭に説明した通り、Firebaseの管理画面でネイティブモードに変更する。

### Functions Setup

```console
A functions directory will be created in your project with a Node.js
package pre-configured. Functions can be deployed with firebase deploy.

? What language would you like to use to write Cloud Functions? 
  JavaScript 
❯ TypeScript 
```

→`Typescript`を選択する。

```console
? Do you want to use TSLint to catch probable bugs and enforce style? (Y/n) 
```

→`Y`を選択する。

```console
? Do you want to install dependencies with npm now? (Y/n) 
```

→`N`を選択する。
`create react app`でインストールしたライブラリが消えて、動作しなくなるため。

### Hosting Setup

```console
Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.

? What do you want to use as your public directory? (public) 
```
→`Enter`(publicを利用する)を押下する。

```console
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) 
```
→SPA用の設定にするため、`y`を入力し、`Enter`を押下する。

```
? File public/index.html already exists. Overwrite? (y/N) 
```
→`create-react-app`が生成したものを利用するため、`N`を入力し、`Enter`を押下する。

### Storage Setup

```console
Firebase Storage Security Rules allow you to define how and when to allow
uploads and downloads. You can keep these rules in your project directory
and publish them with firebase deploy.

? What file should be used for Storage Rules? (storage.rules) 
```

→`Enter`(`storage.rules`を利用する)を押下する。

### Emulators Setup

```
? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i
> to invert selection)
❯◯ Functions
 ◯ Firestore
 ◯ Database
 ◯ Hosting
 ◯ Pubsub
```
→`a`で全て選択する。

```console
? Which port do you want to use for the functions emulator? 5001
? Which port do you want to use for the firestore emulator? 8080
? Which port do you want to use for the database emulator? 9000
? Which port do you want to use for the hosting emulator? 5000
? Which port do you want to use for the pubsub emulator? 8085
? Would you like to download the emulators now? No
```

→ポートは全てデフォルト値とする。
ただし、`Would you like to download the emulators now?`は`No`にする。
なぜか`firebase.json`と`.firebaserc`が作られなかったため。


### 最後に 

最後に以下のようなメッセージが出れば、成功です。

```console
i  Skipping write of public/index.html

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!
```

## 6.2. インストール後の作業

インストール完了後のフォルダ構成は以下の通り。

```console
$ ls -1
README.md
database.rules.json
firebase.json
firestore.indexes.json
firestore.rules
functions
node_modules
package.json
public
src
storage.rules
tsconfig.json
yarn.lock
```

### firebaseホスティングの設定変更
firebaseの設定を修正する。
ローカルの`build`ディレクトリをリモートの`public`にアップロードするように修正する。

```javascript:firebase.json
{
  "hosting": {
    "public": "build", /* publicからbuildに修正する。*/
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}
```

### firestore, database, storageの設定変更
`firestore.rules`、`database.rules.json`、`storage.rules`を修正する。

```json:firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth.uid != null;
    }
  }
}
```

```json:database.rules.json
{
  "rules": {
    ".read": true,
    ".write": true
  }
}
```

```json:storage.rules
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth!=null;
    }
  }
}
```

# 7. 追加のライブラリのインストール
`firebase init`でインストールしなかった、以下のライブラリをインストールする。

```console
$ yarn add firebase-admin firebase-functions
$ yarn add -D @types/firebase
```

# 8. デプロイする
ここまでで一旦、Firebase Hostingにデプロイするので、アプリをビルドします。

```console
$ yarn run build
```

その後、以下のコマンドでFirebase Hostingにデプロイします。

```console
$ firebase deploy
```

# 9. 動作確認

デプロイ後に表示される`Hosting URL`をブラウザで開くとReactの画面が確認できます。

```console
=== Deploying to '*****'...
略
✔  Deploy complete!
Project Console: https://console.firebase.google.com/project/*****/overview
Hosting URL: https://*****.firebaseapp.com
```

<img width="1679" alt="firebasehostingへのデプロイ.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293373/40a4cb8f-0ae5-d90f-2c58-43bc8b73fc85.png">


# 10. 最後に
次回は[画像をアップロードするプログラム](https://qiita.com/tetsurotayama/items/5129f0cfb21f9ec9b9a0)について説明します。

# 11. 関連記事
Reactに関する記事です。

- [第1回 2020年版 Node.js+Reactのインストール](https://qiita.com/tetsurotayama/items/6dd91dc37941fab462a4)
- [第2回 2020年版 ReactのMaterial UI V4の使い方について](https://qiita.com/tetsurotayama/items/12d6c7b3463246306e87)
- [第3回 2020年版 React+Firebaseでアプリを作成する](https://qiita.com/tetsurotayama/items/179daca2cacb0caad0c5)
- [第4回 2020年版 既存のウェブサイトに React を追加する](https://qiita.com/tetsurotayama/items/a2e4edd5bc46bc592c32)
- [第5回 2020年版 ReactのRechartsで新型コロナウイルス感染症対策サイトのデータを可視化する](https://qiita.com/tetsurotayama/items/f5eda6dc2bbcd1f71b4e)
- [第7回 2020年版 React+Firebaseで画像のアップロード（その２）](https://qiita.com/tetsurotayama/items/5129f0cfb21f9ec9b9a0)
- [第8回 2020年版 React+Firebaseで画像のアップロード（その３）](https://qiita.com/tetsurotayama/items/6fb3d3b6e01cac77c85f)
- [第9回 2020年版 ReactにStoryshotsを導入する](https://qiita.com/tetsurotayama/items/dd637e90f4ea008fd878)