iT邦幫忙

2021 iThome 鐵人賽

DAY 18
1

https://ithelp.ithome.com.tw/upload/images/20211003/20120107MZl72V1QFV.png

什麼是 firebase storage

storage 是 firebase 所提供的儲存服務,可以想像是 firebase 版本的雲端硬碟,只不過還加上許多實用的功能。

免費有提供一定的容量和流量,可以放心使用與放心玩

設定 firebase storage

首先在 app.module.ts 引入 AngularFireStorageModule

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AngularFireModule } from '@angular/fire/compat';
// 引入
import { AngularFireStorageModule } from '@angular/fire/compat/storage';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase),
    // 引入
    AngularFireStorageModule
  ],
  declarations: [ AppComponent ],
  bootstrap: [ AppComponent ]
})
export class AppModule {}

再來設定你的 BUCKET

providers: [
    { provide: BUCKET, useValue: environment.firebase.storageBucket },
  ],

這樣就設定完成囉

回到服務,撰寫上傳圖片的功能

再來回到 checkinService 上傳圖片的功能實作部分

 uploadFile(
    imageFiles: File[],
    filePath: string,
    docPath: string,
    message: string,
    name: string
  ): Observable<any> {
    const nowTimestamp = +new Date();
    const fullFilePath = `checkin/${filePath}`;
    let fileArray$ = [];
    for (const [i, imageFile] of Object.entries(imageFiles)) {
      const task = this.storage.upload(
        `${fullFilePath}${nowTimestamp}${i}`,
        imageFile
      );
      fileArray$.push(task);
    }
    return forkJoin(fileArray$).pipe(
      finalize(() => {
        fileArray$.forEach((e, i) => {
          const fileRef = this.storage.ref(
            `${fullFilePath}${nowTimestamp}${i}`
          );
          const downloadURL$ = fileRef.getDownloadURL();
          downloadURL$.subscribe((imageUrl) => {
            if (Number(i) === 0) {
              this.sendMessageToLineChatbot(message, name, imageUrl, filePath);
            }
            this.firestore.doc(docPath).update({
              imgFile: firebase.firestore.FieldValue.arrayUnion(imageUrl),
              docPath: filePath,
            });
          });
        });
      })
    );
  }

總共會使用到5個參數

  1. imageFiles: File[] : 上傳的檔案
  2. filePath: string, :上傳的檔案放在firebase storage的路徑,路徑已存在firestore 建立的id 為主
  3. docPath: string, : firestore上傳的路徑位址,圖片上傳成功後,要將圖片的URL 更新回 firestore 的資料中,記錄下來
  4. message: string, : 圖片上傳成功後,要將打卡人的打卡內容作為訊息傳送到 line message api
  5. name: string : 圖片上傳成功後,要將打卡人的名字做為訊息傳送到 line message api

準備的工作

const fullFilePath = `checkin/${filePath}`;
const nowTimestamp = +new Date();
let fileArray$ = [];
for (const [i, imageFile] of Object.entries(imageFiles)) {
  const task = this.storage.upload(
    `${fullFilePath}${nowTimestamp}${i}`,
    imageFile
  );
  fileArray$.push(task);
}

一開始先定義三個變數

  1. fullFilePath :很好懂,就是要上傳到 firbase storage 的路徑
  2. nowTimestamp: 在上傳的時候,除了以使用者上傳的檔案名稱為主之外,另在檔案名稱後面加上時間戳記,這是為了避免不同使用者上傳圖片時,使用的圖片檔名都一樣(例如: img001),這樣在上傳的時候,會因為同樣的檔名而互相覆蓋,所以加上時間戳記做為區別
  3. fileArray: 將上傳的圖片分別塞到陣列當中,可以看到緊接著就跑了迴圈,將檔案上傳的位置、檔案、通通以可被觀察的對象 (observable) 的形式,放到物件當中

執行階段

準備好之後,就開始執行上傳的工作

return forkJoin(fileArray$).pipe(
  finalize(() => {
    fileArray$.forEach((e, i) => {
      const fileRef = this.storage.ref(`${fullFilePath}${nowTimestamp}${i}`);
      const downloadURL$ = fileRef.getDownloadURL();
      downloadURL$.subscribe((imageUrl) => {
        if (Number(i) === 0) {
          this.sendMessageToLineChatbot(message, name, imageUrl, filePath);
        }
        this.firestore.doc(docPath).update({
          imgFile: firebase.firestore.FieldValue.arrayUnion(imageUrl),
          docPath: filePath,
        });
      });
    });
  })
);

最最最最精華的地方就是使用rxjs的 forkJoin ,解決所有非同步的困擾。forkJoin 代表要陣列裡面所有的可被觀察對象都執行成功後,才會繼續執行下一個動作,也就是所有的圖片都上傳成功之後,才會執行下一動。也就不必煩惱哪個會先上傳完,要做甚麼處理等等。

再來使用 finalize 的運算子,顧名思義,也就是前面都完成之後,最後要做什麼。

上傳完畢之後透過 fileRef 取得上傳位址,再呼叫 getDownloadURL 取得圖片的URL

取得之後,再呼叫 sendMessageToLineChatbot 將打卡訊息、名稱、圖片位置、路徑等參數送出去,傳送到 line message api

最後呼叫firestore 更新的功能,將圖片的資訊更新上去,其中一個比較特殊的功能是 firebase.firestore.FieldValue.arrayUnion ,這個意思是說,檢查陣列裡面的內容,如果重複就不動,沒有的就插入,約等於javascript 的 push 的加強版

好了,這樣上傳圖片的功能就完成了

檢討

最後檢討一下,這樣的寫法是否有問題。

想信眼尖的人都會感到不對勁,為什麼上傳的圖片裡面還會包含傳送line message api的訊息呢?這樣讓功能名稱誤導人又違反單一職責原理。

沒錯,這樣的寫法的確不好,有改善的空間,要快快樂樂地寫 side project,但不是製造麻煩給未來的自己,這一段要改進!


上一篇
DAY17-前後端合體 建立打卡頁面-前端服務篇1
下一篇
DAY19 - 認識 line message API
系列文
做一個面試官無法拒絕的sideproject,當一個全能的前端30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言