Skip to content

AnthonyNahas/ngx-storage-firebaseui

Repository files navigation

ngx-storage-firebaseui - Open Source Library for Angular Web Apps to integrate a material user interface for firebase storage.

npm version demo docs: typedoc codecov CircleCI branch Build Status Join the chat at https://gitter.im/ngx-storage-firebaseui/Lobby dependency Status devDependency Status npm Greenkeeper badge license GitHub forks GitHub stars GitHub followers Twitter URL Twitter Follow Awesome

Angular UI component for firebase authentication. This library is an angular module (including angular components and services) that allows to upload/download or event delete files from firebase storage within a firebase project.

Built by and for developers ❤️

Do you have any question or suggestion ? Please do not hesitate to contact us! Alternatively, provide a PR | open an appropriate issue here

If you like this project, support ngx-storage-firebaseui by starring ⭐ and sharing it 📢

Table of Contents

  • 💝 it uses a responsive and accessible web design UX/UI from google material concepts and components (supporting desktop, tablet and mobile view) incl. smooth animations for a better UX.
  • 💄 pick up your own theme! change the primary, accent and warn colors whenever you need (e.g to support light and dark themes)
  • 🚢 super easy to use with an angular based project (project that is created with the angular-cli)
  • 🔜 optional configuration
  • ♻️ configure your components in runtime
  • ♻️ reusable components for every project that needs some uploads workflows with a firebase project/app.
  • 🛃 built in feedback mechanism in form of a snackbar when an error or any important event occurred.
  • 🆘 ability to delete files
  • 👻 compatible with ngx-auth-firebaseui learn more
  • 🔥 Sync user's uploads with FIRESTORE AUTOMATICALLY
  • 🎉 Supports SSR - Server Side Rendering
  • 🔜 support of i18n
  • <ngx-storage-firebaseui-images> used to upload imaged to firebase storage and sync them with firestore

Supported Processes/Workflows and Actions:

  • upload images
  • image grid
  • delete image (soon)
  • pre-built firebase functions to auto-rotate images (soon)

the full tutorial guide can be found here



"peerDependencies": {
    "@angular/core": "^9.x",
    "@angular/animations": "^9.x",
    "@angular/cdk": "^9.x",
    "@angular/flex-layout": "^9.0.0-beta.29",
    "@angular/material": "^9.x",
    "@angular/fire": "5.4.x",
    "firebase": "7.13.x",
  }

  • Angular (requires Angular 2 or higher, developed with 9.x) - obviously

In the following section, I will show you how to improve the user's experience regarding images

Install firebase extension - Resize Images

  1. install the storage resize images extension in your firebase project
  2. set your firebase cloud function's location
  3. pick either the default bucket or choose another one
  4. set size images: 50x50,100x100,200x200,500x500,1000x1000,2000x2000
  5. choose whether to delete the original image (tip: don't do that so that you can regenerate the thumbnails)
  6. choose the cloud storage path for resized images thumbs (see below an example of an image directory in firebase storage)

after installation

Upload firebase cloud functions to sync the uploaded images to firebase storage with firestore (required)

  1. firebase init
  2. init firebase cloud function in your firebase project (typescript)
  3. copy and paste the below functions
  4. upload firbease deploy --only functions
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { ObjectMetadata } from 'firebase-functions/lib/providers/storage';
// tslint:disable-next-line:no-implicit-dependencies
import { Bucket, Storage } from '@google-cloud/storage';
// @ts-ignore
// tslint:disable-next-line:no-implicit-dependencies
import { GetSignedUrlResponse } from '@google-cloud/storage/build/src/file';


const path = require('path');
const os = require('os');
const fs = require('fs');
// tslint:disable-next-line:no-implicit-dependencies
const spawn = require('child-process-promise').spawn;
// tslint:disable-next-line:no-implicit-dependencies
const mkdirp = require('mkdirp-promise');
const storage = new Storage();


const firestore = admin.firestore();
const defaultStorage = admin.storage();
// const defaultBucket = defaultStorage.bucket();

/**
 * Firebase cloud function to sync uploaded images to firebase storage bucket with firestore
 * 
 * @author Anthony Nahas
 * @since 03.2020
 * @version 1.0 
 */
export const syncImgsWithFirestore =
  functions
    .storage
    .object()
    .onFinalize(async (object: ObjectMetadata) => {

      console.log('syncImgsWithFirestore is now running', object);

      if (object && object.contentType && !object.contentType.includes('image')) {
        console.log('exiting function');
        return false;
      }

      let downloadURLs: any = null;
      let downloadURL = null;
      try {
        downloadURLs = await getDownloadURL(object);
        downloadURL = downloadURLs[0];
        console.log('Download URL --> ', downloadURL, typeof downloadURL);
        // downloadURL = downloadURL[0];
      } catch (e) {
        console.error('Error while getting download url', e);
      }
      const filePath = object.name;
      console.log('file path -->', filePath);
      // @ts-ignore
      const pathSegments = filePath.split('/');
      console.log('path segments --> ', pathSegments);

      const indexOfID = pathSegments.indexOf('imgs');
      console.log('indexOfID', indexOfID, pathSegments[indexOfID + 1]);
      const imgID = pathSegments[indexOfID + 1];
      const lastSegment = pathSegments[pathSegments.length - 1];
      console.log('lastSegment', lastSegment);
      const nameSegments = lastSegment.split('_');
      console.log('nameSegments', nameSegments);
      const resize = nameSegments[nameSegments.length - 1];
      console.log('resize', resize);
      const size = resize.split('x')[0];
      console.log('size', size, typeof size);

      // const img: NahausImage = {
      //   id: imgID,
      //   downloadURL,
      //   path: filePath
      // };

      const indexOfThumbs = pathSegments.indexOf('thumbs');
      console.log('indexOfThumbs', indexOfThumbs);
      let firestorePath: string;
      if (indexOfThumbs !== -1) {
        // the image is a thumbnail
        firestorePath = pathSegments.splice(0, indexOfThumbs).join('/');
        console.log('firestorePath', firestorePath);
        const thumbsMapKey: string = `thumbs.${size}`;
        return await firestore.doc(firestorePath).update({
          [thumbsMapKey]: {
            id: imgID,
            downloadURL,
            path: filePath
          }
        });
      } else {
        pathSegments.pop();
        firestorePath = pathSegments.join('/');
        console.log('firestorePath', firestorePath);
        return await firestore.doc(firestorePath).set({
          id: imgID,
          downloadURL,
          path: filePath
        }, { merge: true });
      }
    });

function getDownloadURL(object: ObjectMetadata): Promise<GetSignedUrlResponse> | void {
  if (!object.name) {
    return;
  }
  const bucket: Bucket = defaultStorage.bucket();
  const file = bucket.file(object.name);

  // Get a signed URL for the file
  return file.getSignedUrl({
    action: 'read',
    expires: '03-17-2025'
  });
}

export const adjustOrientation = functions
  .storage
  .object()
  .onFinalize(async (object) => {
    const filePath = object.name;
    const bucketName = object.bucket;
    const metadata = object.metadata;

    const tempLocalFile = path.join(os.tmpdir(), filePath);
    const tempLocalDir = path.dirname(tempLocalFile);
    const bucket = storage.bucket(bucketName);

    if (object && object.contentType && !object.contentType.startsWith('image/')) {
      console.log('This is not an image.');
      return null;
    }

    if (metadata && metadata.autoOrient) {
      console.log('This is already rotated');
      return null;
    }

    return mkdirp(tempLocalDir).then(() => {
      // Download file from bucket.
      // @ts-ignore
      return bucket.file(filePath).download({ destination: tempLocalFile });
    }).then(() => {
      console.log('The file has been downloaded to', tempLocalFile);
      // Convert the image using ImageMagick.
      return spawn('convert', [tempLocalFile, '-auto-orient', tempLocalFile]);
    }).then(() => {
      console.log('rotated image created at', tempLocalFile);
      // @ts-ignore
      metadata.autoOrient = true;
      return bucket.upload(tempLocalFile, {
        destination: filePath,
        metadata: { metadata: metadata }
      });
    }).then(() => {
      console.log('image uploaded to Storage at', filePath);
      // Once the image has been converted delete the local files to free up disk space.
      fs.unlinkSync(tempLocalFile);
      console.log('Deleted local file', filePath);
      return;
    });
  });

1. Install via ng add. (Recommended)

If Angular Material Design is not setup, just run ng add @angular/material learn more

Now add the library via the angular schematics

ng add ngx-storage-firebaseui
  • ✔️ peer dependencies will be automatically added the package.json and installed
  • ✔️ ngx-storage-firebaseui 's module will be automatically imported to the root module (just replace PUT_YOUR_FIREBASE_API_KEY_HERE with your firebase api key)
  • ✔️ ngx-storage-firebaseui 's assets will be automatically added the angular.json file

2. Install via npm. (Alternative)

Install above dependencies via npm.

Now install ngx-storage-firebaseui via:

npm install --save ngx-storage-firebaseui
npm i -s @angular/material @angular/cdk @angular/flex-layout @angular/forms @angular/animations @angular/router

Firebase deps

npm i -s firebase @angular/fire

-> continue by following the instructions here

Once installed you need to import the main module:

import { NgxStorageFirebaseuiModule } from 'ngx-storage-firebaseui';

The only remaining part is to list the imported module in your application module. The exact method will be slightly different for the root (top-level) module for which you should end up with the code similar to (notice NgxStorageFirebaseuiModule .forRoot()):

and then from your Angular AppModule:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

// Import your library
import { NgxStorageFirebaseuiModule } from 'ngx-storage-firebaseui';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,

    // Specify the ngx-storage-firebaseui library as an import
    NgxStorageFirebaseuiModule.forRoot({
                    apiKey: 'your-firebase-apiKey',
                    authDomain: 'your-firebase-authDomain',
                    databaseURL: 'your-firebase-databaseURL',
                    projectId: 'your-firebase-projectId',
                    storageBucket: 'your-firebase-storageBucket',
                    messagingSenderId: 'your-firebase-messagingSenderId'
                }),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Other modules in your application can simply import NgxStorageFirebaseuiModule:

import { NgxStorageFirebaseuiModule } from 'ngx-storage-firebaseui';

@NgModule({
  declarations: [OtherComponent, ...],
  imports: [NgxStorageFirebaseuiModule, ...],
})
export class OtherModule {
}

SystemJS

Note:If you are using SystemJS, you should adjust your configuration to point to the UMD bundle. In your systemjs config file, map needs to tell the System loader where to look for ngx-storage-firebaseui:

{
  'ngx-storage-firebaseui';: 'node_modules/ngx-storage-firebaseui/bundles/ngx-storage-firebaseui.umd.js',
}

(3) Usage

Once the library is imported, you can use its components, directives and pipes in your Angular application:

<ngx-storage-firebaseui-images></ngx-storage-firebaseui-images> see the usage

<ngx-storage-firebaseui-images></ngx-storage-firebaseui-images> see the api

option bind type default description
path Input() string - path to store the image into firebase storage as well as in the firestore
load Input() boolean true whether to load the uploaded images from firebase
addButtonTooltipText Input() string add picture text of the add button tooltip
onLoad Output() number - called when the images are loaded from firestore
onImageUploaded Output() number - called when an image is uploaded to firebase storage
onImageSelectedAtIndex Output() number - called when an image is selected / clicked

Other Angular Libraries


Built by and for developers ❤️ we will help you 👊


jetbrains logo

This project is supported by jetbrains with 1 ALL PRODUCTS PACK OS LICENSE incl. webstorm


Copyright (c) 2020 Anthony Nahas. Licensed under the MIT License (MIT)