angular - 如何将照片上传到 Firebase 存储并在 Firestore 中插入照片 URL?

标签 angular firebase ionic-framework google-cloud-firestore

本模块的主要目标是在 Firebase 存储中上传用户的个人资料图片,并将照片 URL 存储在 Firestore (Cloud Firestore) 中。

注意:

  1. 我上传的图片只有 JPG 和 PNG 格式。
  2. 这是包含 Ionic 和 Angular 的跨平台应用程序

我尝试以下场景:

  1. 直接将图片推送到 Firestore 中,而不将其上传到 Firebase Cloud Storage,因此图片名称会转换为长字符“data:image,base64”> 其中,我不知道这种情况在我的数据库或系统中是否可行,因为它运行完美,并且图像是可检索的。我希望您能在这种情况下帮助我。

更新的错误:

New Error

这是用户的字段:

User fields

update-profile.component.ts

  userId: string;
  fname: string;
  studentId: string;


  @ViewChild('filePicker') filePickerRef: ElementRef<HTMLInputElement>;
  @Input() showPreview = true;

  //The Selected Image in the File Explorer
  selectedImage: any;


   constructor(private auth: AuthService,
              private afs: AngularFirestore,
              private loadingCtrl: LoadingController,
              private toaster: ToastController,
              private storage: AngularFireStorage,
              private popOverCtrl: PopoverController) { }

  ngOnInit() {

    this.profileEditSub = this.auth.user$.subscribe(user => {
      this.userId = user.userId;
      this.fname = user.userName;
      this.studentId = user.userSchoolId;
      this.selectedImage = user.userPhoto;
    });

  }

  onSubmit(form: NgForm){

    const user = form.value.fname;
    const studentId = form.value.studentId;

    this.onUpdateUser(user, studentId);

  }//

  async onUpdateUser(name: string, studentNo: string){
    const loading = await this.loadingCtrl.create({
      message: 'Updating...',
      spinner: 'crescent',
      showBackdrop: true
    });

    
    loading.present();
    
    const imageUrl = await this.uploadFile(this.userId, this.selectedImage);

    this.afs.collection('user').doc(this.userId).update({
      'userName': name,
      'userSchoolId': studentNo,
      'userPhoto': imageUrl, // Photo URL from Firebase Storage will be saved in here.
      'editedAt': Date.now()
    })
    .then(() => {
      loading.dismiss();
      this.toast('Update Success', 'success');
      this.closePopOver();
    })
    .catch(error => {
      loading.dismiss();
      this.toast(error.message, 'danger');
    })

  }//

  async uploadFile(id, files):Promise<any> {
    if (files && files.length) {
      try {
        const task = await this.storage.ref('images').child(id).put(files[0]);
        return this.storage.ref(`images/${id}`).getDownloadURL().toPromise();
      } catch (error) {
        console.log(error);
      }
    }
  }

  onPickImage() {
    this.filePickerRef.nativeElement.click();
  }

  onFileChosen(event: Event) {
    const pickedFile = (event.target as HTMLInputElement).files[0];

    if (!pickedFile) {
      return;
    }

    const fr = new FileReader();
    fr.onload = () => {
      const dataUrl = fr.result.toString();
      this.selectedImage = dataUrl;
    
    };
    fr.readAsDataURL(pickedFile);
  }

update-profile.component.html

  <!-- For the Image -->
  <div class="picker">
    <img
      class="img"
      [src]="selectedImage"
      *ngIf="selectedImage && showPreview"
    >
  </div>
  <input type="file" accept=".jpg,.png" *ngIf="usePicker" #filePicker (change)="onFileChosen($event)"/>

  <ion-button class="image-btn" (click)="onPickImage()">
    <ion-icon name="camera" slot="icon-only"></ion-icon>
  </ion-button>

  <!-- Other Textual Information -->
  <form #f="ngForm" (ngSubmit)="onSubmit(f)">
    <ion-list lines="full">
      <ion-item>
        <ion-label position="floating">Full Name:</ion-label>
        <ion-input name="fname" required type="text" [(ngModel)]="fname" #userCtrl="ngModel"></ion-input>
      </ion-item>
      <ion-item>
        <ion-label position="floating">Student No:</ion-label>
        <ion-input name="studentId" required type="number" [(ngModel)]="studentId" #studentIds="ngModel"></ion-input>
      </ion-item>

      <ion-button class="ion-margin" type="submit" expand="block" shape="round" [disabled]="!f.valid">Edit User</ion-button>
    </ion-list>
  </form>

最佳答案

我在您的代码中注意到了一些事情。

  1. 您打算使用 this.selectedImage <input> 的选定文件上的变量标签(来自文件资源管理器的注释),但在下面的行中,您将其重新分配给 firestore 中图像的存储引用。
this.profileEditSub = this.auth.user$.subscribe((user) => {
  ...
  this.selectedImage = user.userPhoto; // this line
});
  • 您不需要 FileReader.readAsDataURL() (除非您想使用 Reference.putString() )。根据Firebase Storage docs,您可以直接上传 File API对象,只需将其作为 storage.ref().put(File) 中的参数传递即可。 Example from Firebase docs.
  • 建议

    • src 使用单独的变量<img>的,然后使用另一个变量来存储文件选择器的当前值 ( <input type="file" /> )。

    • 使用 onFileChosen()仅用于为文件资源管理器中选定的文件分配变量的回调。

    update-profile.component.ts

    currentImageUrl: string; // to store the downloadUrl of image to be displayed
    selectedFiles: Array<File>; // to store selected files in the File Explorer
    
    this.profileEditSub = this.auth.user$.subscribe(async (user) => {
      ...
    
      /** 
       * you only store the storage reference (user.userPhoto) in firestore, not the download url,
       * so you need to fetch it again
       */
      this.currentImageUrl = await this.storage
        .ref(user.userPhoto)
        .getDownloadURL()
        .toPromise();
    });
    
    onFileChosen(event: any) {
        this.selectedFiles = event.target.files; // just assigns the selected file/s in <input> this.selectedFiles
    }
    
    async onSubmit() {
        if (this.selectedFiles.length) {
            // Get selected file
            const file = this.selectedFiles[0];
    
            // Get the fullPath in Storage after upload
            const fullPathInStorage = await this.uploadImage(this.userId, file);
    
            /**
             * You can now store the fullPathInStorage in firestore
             *
             * afs.update(...)
             */
    
            // Get the downloadUrl for the src of img
            this.currentImageUrl = await this.storage
                .ref(fullPathInStorage)
                .getDownloadURL()
                .toPromise();
        }
    }
    
    async uploadImage(uid, file): Promise<string> {
        /**
         * You can add random number in file.name to avoid overwrites,
         * or replace the file.name to a static string if you intend to overwrite
         */
        const fileRef = this.storage.ref(uid).child(file.name);
    
        // Upload file in reference
        if (!!file) {
          const result = await fileRef.put(file);
    
          return result.ref.fullPath;
        }
    }
    

    update-profile.component.html

    <input type="file" (change)="onFileChosen($event)"/>
    
    <img [src]="currentImageUrl" />
    

    此外,ERR_NAME_NOT_RESOLVED可能只是由于网络不稳定所致。

    关于angular - 如何将照片上传到 Firebase 存储并在 Firestore 中插入照片 URL?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66932550/

    相关文章:

    angularjs - 将 Ionic 部署为网站

    javascript - Angular 4 模板中的 bool 值

    xcode - Firebase_core, flutter 构建错误

    android - 或 Firestore 查询中的逻辑操作

    firebase - 复制Firebase存储上的文件?

    javascript - $ionicPlatform onHardwareBackButton 运行多次

    css - 如何自定义ionic 4侧边菜单?

    angular - 如何使用 json 数据填充 Angular Material 数据表?

    javascript - MatDatepicker : No provider found for DateAdapter. 您必须在应用程序根目录中导入以下模块之一:MatNativeDateModule

    angular - 如何在 Angular CLI 中为生产构建启用 gzip 压缩