iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 19
0
Modern Web

Angular Firebase 入門與實做系列 第 19

[Angular Firebase 入門與實做] Day-18 Cloud Functions Cloud Storage Triggers 02

每日一句來源:Daily English

The first and greatest victory is to conquer yourself; to be conquered by yourself is of all things most shameful and vile. -- 戰勝自己是最首要、最偉大的勝利,無法克制自己是最令人羞愧、最可憐的失敗。

今天我們就來使用昨天寫的trigger來實做Angular看看,實做一個使用者自行上傳照片的功能

我們今天會使用ngxf-uploader來實做檔案上傳的功能,
npm i ngxf-uploader
相關的API可以點網址過去觀看。

透過他我們可以簡單的出脫動檔案,上傳檔案的功能

首先加入module

...
import { NgxfUploaderModule } from 'ngxf-uploader';

@NgModule({
  imports: [
    // 由於我們沒有要使用httpClient來上傳,我們使用firebase的方法,因此我們不使用他提供的service
    NgxfUploaderModule 
    ...
  ],
  declarations: [
    ...
  ]
})
export class UserModule { }

接著我們就能在component使用他的API了

<button mat-raised-button color="primary" (click)="fileSelect.click()">
  上傳檔案
</button>

<div class="block"
     (ngxf-drop)="uploadFile($event)"
     drop-class="drop"
     accept="image/*,.svg" multiple>
  <label class="upload-button">
    choice file.
  </label>
</div>
<input type="file" #fileSelect
  (ngxf-select)="uploadFile($event)"
  [ngxf-validate]="{ size: { min: 50, max:1000000 } }"
  accept="image/*,.svg">

要注意如果有使用multiple的話她回傳的會是一個 File[],在使用上要注意,accept在drop上也可以給

ts

// 注入錯誤訊息的型別
import { FileError } from 'ngxf-uploader';
...

uploadFile(file: File | FileError): void {
  console.log(file);
  // 判斷是檔案還是錯誤訊息
  if (!(file instanceof File)) {
    this.alertError(file);
    return;
  }
  const filePath = `/users/${new Date().getTime()}_${file.name}`;
  this.fileHandler = this._upload.fileHandler(filePath);

  this.fileHandler.upload({ file: file })
    .subscribe(RxViewer);

  this.uploadPercent$ = this.fileHandler.task.percentageChanges();
  this.fileURL$ = this.fileHandler.task.downloadURL();
  this.meta$ = this.fileHandler.task.snapshotChanges().map(d => d.metadata);
}
  

我們可以加入這個判斷來知道透過ngxf-uploader獲得了什麼資訊

alertError(msg: FileError) {
  switch (msg) {
    case FileError.NumError:
      alert('Number Error');
      break;
    case FileError.SizeError:
      alert('Size Error');
      break;
    case FileError.TypeError:
      alert('Type Error');
      break;
  }
}

接著我們繼續修改聊天室為她加上檔案上傳的功能

HTML

我們一樣擺上input並給定ngxf-select並且設定相關限制

<input type="file" #fileSelect
  (ngxf-select)="uploadFile($event)"
  [ngxf-validate]="{ size: { min: 50, max:1000000 } }"
  accept="image/*,.svg">  

也為整個聊天區塊加上拖曳的功能,並且一樣綁上參數,在增加一個當我們拖曳過去後的效果大概像這樣

<section class="message-container" fxLayout="column nowrap"
  (ngxf-drop)="uploadFile($event)"
  drop-class="drop"
  accept="image/*,.svg">

  <div class="block">
    <mat-icon class="mat-size-5 c-white">cloud_upload</mat-icon>
  </div>
  ...

再來修改送出訊息的地方,因為在form裡面,我們要給定type="button"才不會觸發這個form的送出事件,並且給予click事件點擊我們的input file。

<div class="t-al-c pad-l-r-1">
  <button mat-icon-button type="button" (click)="fileSelect.click()">
    <mat-icon color="accent">add_to_photos</mat-icon>
  </button>
  <button mat-icon-button>
    <mat-icon color="accent">send</mat-icon>
  </button>
</div>

TS

接著來到TS的部分,我們可以將fileErrorHandler搬移至我們的upload.service.ts,並且加上我們的警告視窗。

@Injectable()
export class UploadService {

  constructor(
    private _storage: AngularFireStorage,
    private _alc: AlertConfirmService) { }

  fileHandler<T>(path: string) {
    return new FileHandler<T>(this._storage, path);
  }

  fileErrorHandler(errror: FileError) {
    switch (errror) {
      case FileError.NumError:
        this._alc.alert('檔案數量錯誤');
        break;
      case FileError.SizeError:
        this._alc.alert('檔案大小錯誤');
        break;
      case FileError.TypeError:
        this._alc.alert('檔案格式錯誤');
        break;
    }
  }
}

由於我們現在多了檔案類型的訊息,我們可以定義一個列舉來存放種類

message.model.ts

export enum MESSAGE_TYPE {
  MESSAGE = 'message',
  FILE = 'file'
}

export interface MessageModel extends BaseModel {
  sender: string;
  addressee: string;
  content: string;
  type: MESSAGE_TYPE;
}

接著回到component,加上uploadFile的方法,得到檔案後先做錯誤的判斷,接著我們實作上傳檔案


uploadFile(file: File | FileError) {
  if (!(file instanceof File)) {
    this._upload.fileErrorHandler(file);
    return;
  }

  const filePath = `${new Date().getTime()}_${file.name}`;
  const fileHandler = this._upload.fileHandler(filePath);

  // 我們這裡把兩個rx一起送出,並且為我們的訊息加上類型
 return this.getMessageObs(filePath, MESSAGE_TYPE.FILE)
  .mergeMap(() => fileHandler.upload({ file: file }))
  .subscribe(RxViewer);
}

接著我們可以測試看看是否上傳成功,並建立縮圖。

目前筆者測試很奇怪有時圖片是不會產生的,或許是目前storage的trigger感覺尚存在問題,且從訊息中心那並未得到任何錯誤,感覺像是並且完全沒有觸發,我想是尚有BUG,也或是因為筆者是免費版本的關係,但由於沒有錯誤訊息,暫時無法了解原因,若將來筆者有所了解再告知大家,不過我們這裡至少知道了有此功能能使用。

最後我們可以使用先前教過大家的pipe來顯示檔案,並且當檔案載入中的時候給予一個小動畫

<ng-container *ngIf="message.content | img | async as img; else imgloading">
  <div class="message-img mat-elevation-z2"
    [style.backgroundImage]="img | safe:'background-image'">
    <img  [src]="img">
  </div>
</ng-container>
<ng-template #imgloading>
  <div class="message-img" fxLayoutAlign="center center">
    <mat-progress-spinner mode="indeterminate" color="accent" [diameter]="20"></mat-progress-spinner>
  </div>
</ng-template>

我們會發現資料並無法正確的顯示,因為當我們的trigger在執行時,我們的檔案實際上是尚未建立完成的,若這時我們去取得this._storage.ref(path).getDownloadURL()當然就得到空的了,因此我們這裡還需要下一點功夫,明天再跟大家進一步了解解決辦法。

本日小節

今天使用ngxf-uploader來實做檔案上傳及拖曳上傳,簡單的實作並結合trigger實作縮圖,讓我們在顯示使用這圖片的時候可以使用小縮圖,降低使用者載入的速度,筆者有注意到,目前storage的trigger感覺尚存在許多問題,筆者會發現有時縮圖並不會產生,並且完全沒有觸發,我想是尚有BUG,或是因為我們是免費版本的關係,但由於沒有錯誤訊息,暫時無法了解原因,若將來筆者有所了解再告知大家,明天我們將進一步解決圖片載入的問題。

參考文章

https://github.com/ZouYouShun/ngxf-uploader
https://ngxf-uploader.firebaseapp.com/upload


上一篇
[Angular Firebase 入門與實做] Day-17 Cloud Functions Cloud Storage Triggers
下一篇
[Angular Firebase 入門與實做] Day-19 Cloud Functions Cloud Storage Triggers 03 修正聊天室顯示功能
系列文
Angular Firebase 入門與實做31

尚未有邦友留言

立即登入留言