DAY18 - 将档案上传到 firebase storage

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,但不是制造麻烦给未来的自己,这一段要改进!


<<:  Day-18 任意举出三个你在开发 Rails 专案时常用到的 gem,并说明一下

>>:  30-18 之 DataSource Layer- DataMapper

Day14:全端工程师的工作内容?(上)

一、前言   因为我待的是较小型的接案公司,基本上全端工程师的工作几乎全包,从投标、接案、访谈客户需...

【Day 12】Array 阵列

Array Array其实也是一种变数型态,不过是 nonbasic data type。 宣告阵列...

Day 29: 跨平台比较

Keyword: Flutter 、React Native、KMM 对於只要一份Code就能部署到...

Day1 用python写UI-前言

哈罗,大家好,我是即将要升大三的老屁股Ψ( ̄∀ ̄)Ψ,在一切因缘际会下决定要做用python写UI...

转行如何学Python ?

最近在准备转行学python,此前学过一段时间JAVA,想来问问大神们有什么好的资料分享吗?之前在其...