Jawad Farooqi
Jawad Farooqi

Reputation: 345

How to get the download Url of firebase file storage

I can't understand the process of getting the download URl, Can someone please break it down to me nicely? So I have this upload component here:

import { Component, OnInit } from '@angular/core';
import { AngularFireStorage, AngularFireUploadTask } from 
'angularfire2/storage';
import { AngularFirestore } from 'angularfire2/firestore'; 
import { Observable } from 'rxjs/Observable';
import { tap, filter, switchMap } from 'rxjs/operators';
import { storage } from 'firebase/storage';


@Component({
  selector: 'file-upload',
  templateUrl: './file-upload.component.html',
  styleUrls: ['./file-upload.component.scss']
})
export class FileUploadComponent {

  // Main task   
  task: AngularFireUploadTask;

  // Progress monitoring
  percentage: Observable<number>;

  snapshot: Observable<any>;

  // Download URL
  downloadURL: Observable<string>;

  // State for dropzone CSS toggling 
  isHovering: boolean;

  constructor(private storage: AngularFireStorage, private db: AngularFirestore) { }

  toggleHover(event: boolean) {
    this.isHovering = event;
  }

  startUpload(event: FileList) {
    // The File object
    const file = event.item(0)

    // Client-side validation example
    if (file.type.split('/')[0] !== 'image') { 
      console.error('unsupported file type :( ')
      return;
    }

    // The storage path
    const path = `test/${new Date().getTime()}_${file.name}`;

    // Totally optional metadata
    const customMetadata = { app: 'My AngularFire-powered PWA!' };

    // The main task
    this.task = this.storage.upload(path, file, { customMetadata })

    // Progress monitoring
    this.percentage = this.task.percentageChanges();
    this.snapshot   = this.task.snapshotChanges().pipe(
      tap(snap => {
        console.log(snap)
        if (snap.bytesTransferred === snap.totalBytes) {
          // Update firestore on completion
          this.db.collection('photos').add( { path, size: snap.totalBytes })
        }
      })
    )


    // The file's download URL
    this.downloadURL = this.task.downloadURL(); 
    console.log(this.downloadURL)

  const ref = this.storage.ref(path);
  this.task = ref.put(file, {customMetadata});

  this.downloadURL = this.task.snapshotChanges().pipe(
    filter(snap => snap.state === storage.TaskState.SUCCESS),
    switchMap(() => ref.getDownloadURL())
  )
  console.log(this.downloadURL);
}

  // Determines if the upload task is active
  isActive(snapshot) {
    return snapshot.state === 'running' && snapshot.bytesTransferred < snapshot.totalBytes
  }}

I try to console the supposed way of getting the download URL but it's empty, i've seen some other ways to get it done but can't seem to get it right. Download URL is always null even with snapshot.downloadURL. Following is the package.json:

{
  "name": "storage-app",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^6.0.3",
    "@angular/common": "^6.0.3",
    "@angular/compiler": "^6.0.3",
    "@angular/core": "^6.0.3",
    "@angular/forms": "^6.0.3",
    "@angular/http": "^6.0.3",
    "@angular/platform-browser": "^6.0.3",
    "@angular/platform-browser-dynamic": "^6.0.3",
    "@angular/platform-server": "^6.0.3",
    "@angular/router": "^6.0.3",
    "angularfire2": "5.0.0-rc.6",
    "core-js": "^2.5.4",
    "firebase": "4.12.1",
    "rxjs": "^6.0.0",
    "rxjs-compat": "^6.2.2",
    "zone.js": "^0.8.26"
  },
  "devDependencies": {
    "@angular/cli": "^6.0.8",
    "@angular/compiler-cli": "^6.0.3",
    "@angular/language-service": "^6.0.3",
    "@types/jasmine": "~2.5.53",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "jasmine-core": "~2.6.2",
    "jasmine-spec-reporter": "~4.1.0",
    "karma": "~1.7.0",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~3.2.0",
    "tslint": "~5.7.0",
    "typescript": "~2.7.2",
    "@angular-devkit/build-angular": "~0.6.8"
  }
}

Thanks in advance

Upvotes: 2

Views: 3942

Answers (3)

Miki
Miki

Reputation: 1818

Angularfire provides this super convenient pipe, getDownloadURL:

<img [src]="'users/davideast.jpg' | getDownloadURL" />

Official docs.

The end.

Upvotes: 0

SiddAjmera
SiddAjmera

Reputation: 39482

Here's a simple example to help you understand how(taken from AngularFire2 GitHub):

uploadPercent: Observable < number > ;
downloadURL: Observable < string > ;

constructor(
  private storage: AngularFireStorage
) {
}

uploadFile(event) {
  const file = event.target.files[0];
  const filePath = 'files';
  const fileRef = this.storage.ref(filePath);
  const task = this.storage.upload(filePath, file);

  // observe percentage changes
  this.uploadPercent = task.percentageChanges();
  // get notified when the download URL is available
  task.snapshotChanges().pipe(
      finalize(() => this.downloadURL = fileRef.getDownloadURL())
    )
    .subscribe()
}

And here's the template for this:

<input type="file" (change)="uploadFile($event)" />
<div>{{ uploadPercent | async }}</div>
<a [href]="downloadURL | async">{{ downloadURL | async }}</a>

Here's what's happening:

We're acting upon the change event of File Input. Once that is done, we'll get the file to upload. We're then creating a file reference on our Firebase storage by calling ref and passing it the path to our file. This will be helpful later in retrieveing the File Download URL.

After that, we're creating an AngularFireUplaodTask by calling upload on storage and passing it the filepath and the file to be uploaded.

On this task, we can check for the file upload percentage by calling percentageChanges on the upload task. This is again an Observable and thus we're listening for and printing changes on the DOM by using the async pipe.

finalize will get triggered when the upload task is done. And that's when we'll be able to get the download url by calling getDownloadURL on the fileRef that we created earlier.

You can have a look at this StackBlitz for more info.

Upvotes: 0

DarkLeafyGreen
DarkLeafyGreen

Reputation: 70466

You can retrieve the download url from the storage ref:

loading = false;

uploadFile(event) {

    this.loading = true;

    const file = event.target.files[0];
    // give it a random file name
    const path = Math.random().toString(36).substring(7); 
    const storageRef = this.storage.ref(path);
    const task = this.storage.upload(path, file);

    return from(task).pipe(
      switchMap(() => storageRef.getDownloadURL()),
      tap(url => {
          // use url here, e.g. assign it to a model
      }),
      mergeMap(() => {
          // e.g. make api call, e.g. save the model 
      }),
      finalize(() => this.loading = false)
    ).subscribe(() => {
      // success
    }, error => {
      // failure
    });
  }

I am using angularfire 5.0.0-rc.11

Upvotes: 2

Related Questions