AngularでCloud Storage for Firebaseにファイルアップロード(キャッシュとか権限とか)

# 記事の目的
Firebase便利ですよね。
なかでも簡単にファイルアップロードは便利です。

こちらに手順を記載されている記事があります
https://qiita.com/daikiojm/items/1c5940d618b3b644439a

- downloadUrlが非推奨になっていること
- キャッシュの設定とか
- 権限を少し

の追記だけしておきます。


## 早速コードの箇所だけ

Material Designを利用していますが、使わなくても大丈夫です。
（ボタンをおしゃれにするために、inputフィールドを隠しています。）

```Html
<!-- テキストファイルを選択するinput -->
<div id="input-area">
  <input #fileUpload hidden type="file" value=""
         (change)="selectFile($event)"/>
  <button
      (click)="fileUpload.click()"
      type="button"
      mat-button color="primary">ファイル選択
  </button>
  <p *ngIf="selectedFile">アップロードファイル: {{selectedFile.name}}</p>
  <button [disabled]="!selectedFile"
          mat-stroked-button color="primary"
          (click)="upload();">Upload
  </button>
</div>

<!-- アップロードしたファイルを表示するエリア -->
<a *ngIf="uploadFileUrl" [href]="uploadFileUrl">{{ uploadFileUrl }}</a>
```


```TypeScript

  // selected file
  public selectedFile;

  // ファイルのURL
  public uploadFileUrl;

  constructor(
    private storage: AngularFireStorage,
    private store: AngularFirestore,
  ) {
  }

  selectFile(evt: any) {
    // ファイルを未選択で閉じた場合など。
    if (!evt.target || !evt.target.files || evt.target.files.length !== 1) {
      this.selectedFile = null;
      return;
    }
    this.selectedFile = evt.target.files[0];
  }

  async upload() {
    if (!this.selectedFile) {
      return;
    }

    // ファイルをUIDごとに分けるとか
    const filePath = 'uploads/' + 'uid等でフォルダを分ける' + '/' + this.selectedFile.name;

    // ストレージの参照をもつ
    const storageRef = this.storage.ref(filePath);

    // putまたはputStringであげる
    const result = await storageRef.put(this.selectedFile, {'cacheControl': 'public, max-age=86400'});
    console.log(result);

    // storageの参照からダウンロードURLを取得（先の例にあったようにobservableのまま受け取ってもOK）
    storageRef.getDownloadURL().subscribe((result) => {
      this.uploadFileUrl = result;
      // ここでStoreにユーザがアップロードしたファイル一覧に保持とか
    });
  }

```

ほぼ同じ方法で
blobやbase64もできます。
https://firebase.google.com/docs/storage/web/upload-files?hl=ja

```TypeScript
    const result = await storageRef.put(this.selectedFile, {'cacheControl': 'public, max-age=86400'});
```

ここで例えばキャッシュをもたせる場合は、
上記のように、putのオプションとして設定可能です。

## FirebaseStorageの権限について

```Text
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read;
    }
    match /uploads/{allPaths=**} {
        allow read;
        allow create: if request.auth != null && request.resource.size <= 10 * 1024 * 1024;
        allow update: if request.auth.uid == resource.data.uid && request.resource.size <= 10 * 1024 * 1024;
        allow delete: if request.auth.uid == resource.data.uid;
    }
  }
}

```

```Text
    match /{allPaths=**} {
      allow read;
    }
```

最初に任意のパスへの書き込みは規制した方が良いかと思います。

```
    match /uploads/{allPaths=**} {
        allow read;
        allow create: if request.auth != null && request.resource.size <= 10 * 1024 * 1024;
        allow update: if request.auth.uid == resource.data.uid && request.resource.size <= 10 * 1024 * 1024;
        allow delete: if request.auth.uid == resource.data.uid;
    }
```

続いて、uploads配下（ここは利用したいパスによります）に関しての権限を決めます。
例として、プロフィール画像のアップロードの場合、

- read: 読み込みは誰でもOK　→　if はなければ常にTrue扱い
- create: 認証済みユーザなら登録可なので、request.authがnullじゃないこと＝認証済みユーザであること
を確認（firebase authenticationを利用して下さい）
- update: データを登録したユーザと変更者のUIDが一致していたら変更可とする（firebase authenticationを利用して下さい）
- delete: データを登録したユーザと削除者のUIDが一致していたら削除可とする（firebase authenticationを利用して下さい）

追加で、createとuploadの際に10MB以下としてあります。
（大きいファイルをがんがん上げられるのを防ぐため）



## 雑ですいません。
Angular最高！！

詳細は以下に有ります。

https://github.com/angular/angularfire/blob/master/docs/storage/storage.md
あと使いそうなところだと

```TypeScript
    // observe percentage changes
    this.uploadPercent = task.percentageChanges();
    // get notified when the download URL is available
    task.snapshotChanges().pipe(
        finalize(() => this.downloadURL = fileRef.getDownloadURL() )
     )
    .subscribe()
```

ここの箇所でUpload時のプログラスバーを出したいときなど便利です。