6
votes

I've been going through the angularfire2 documentation to retrieve a downloadURl from storage. I'm hoping I'm missing something simple here.

The documentation states:

@Component({
  selector: 'app-root',
  template: `<img [src]="profileUrl | async" />`
})
 export class AppComponent {
   profileUrl: Observable<string | null>;
   constructor(private storage: AngularFireStorage) {
   const ref = this.storage.ref('users/davideast.jpg');
   this.profileUrl = ref.getDownloadURL();
 }
}

However, once I've uploaded an image I want to return the download url as a string to upload to firestore. I need the download URL for an external service.

My function

uploadImage(base64data) {

  const filePath = (`myURL/photo.jpg`);
  const storageRef = firebase.storage().ref();

  var metadata = {
    contentType: 'image',
    cacheControl: "public, max-age=31536000",
  };

  const ref = this.storage.ref(filePath);
  const task = ref.putString(base64data, 'data_url', metadata).then(() => {

    var downloadURL = ref.getDownloadURL();

  })

}

This uploads the image perfectly fine. However, I would then like to write the download URL to firestore. When console logging my 'downloadURL' variable, I get the following:

PromiseObservable {_isScalar: false, promise: y, scheduler: undefined}

The download is inside the promise observable. How do I just get the download URL string as my variable? Once I have that I can sort the firestore updates out.

4

4 Answers

2
votes

This answer is not relevant from Firebase 5.0 release, they removed downloadURL() from upload task. Please refer to doc.

The .downloadURL() observable emits the download URL string once the upload is completed. Then you need to subscribe to get the value.

uploadImage(base64data) {

  const filePath = (`myURL/photo.jpg`);
  //const storageRef = firebase.storage().ref();

  var metadata = {
    contentType: 'image',
    cacheControl: "public, max-age=31536000",
  };

  const ref = this.storage.ref(filePath);
  const task = ref.putString(base64data, 'data_url', metadata);
  const downloadURL = task.downloadURL();

  downloadURL.subscribe(url=>{
     if(url){
         console.log(url);
         //wirte the url to firestore
     }
  })

}

Hope this helps. check this blog for more detail

8
votes
//observable to store download url
downloadURL: Observable<string>;

task.snapshotChanges().pipe(
    finalize(() => {
        this.downloadURL = fileRef.getDownloadURL();
        this.downloadURL.subscribe(url=>{this.imageUrl = url})
    })
)

refer :https://github.com/ReactiveX/rxjs/blob/master/doc/observable.md

2
votes

.downloadURL() doesn't works longer anymore, you need to use .getDownloadURL() combined with finalize() like so:

.html file

<input type="file" (change)="uploadFile($event)">

.ts file

import {
  AngularFireStorage,
  AngularFireStorageReference,
  AngularFireUploadTask
} from '@angular/fire/storage';
import { Component } from '@angular/core';
import { finalize } from 'rxjs/operators';

@Component({
  selector: 'app-upload',
  templateUrl: './upload.component.html',
  styleUrls: ['./upload.component.scss']
})
export class UploadComponent {
    constructor(private angularFireStorage: AngularFireStorage) {}

    public uploadFile(event: any): void {
        for (let i = 0; i < event.target.files.length; i++) {
            const file = event.target.files[i];
            const fileRef: AngularFireStorageReference = this.angularFireStorage.ref(
              file.name
            );
            const task: AngularFireUploadTask = this.angularFireStorage.upload(
              file.name,
              file
            );
            task
                .snapshotChanges()
                .pipe(
                finalize(() => {
                    fileRef.getDownloadURL().subscribe(downloadURL => {
                        console.log(downloadURL);
                    });
              })
          )
          .subscribe();
       }
   }
}  

Also, note the @angular/fire, it's because all AngularFire2 package is moving into @angular/fire and this is the recommended way to use from now onwards.

2
votes

Nesting subscriptions is an antipattern so instead of subscribing in finalize you should use last + switchMap or concat + defer.

last + switchMap

task.snapshotChanges().pipe(
  last(),
  switchMap(() => fileRef.getDownloadURL())
).subscribe(url => console.log('download url:', url))

concat + defer

concat(
  task.snapshotChanges().pipe(ignoreElements()),
  defer(() => fileRef.getDownloadURL())
).subscribe(url => console.log('download url:', url))