DEV Community

Plinio Grijalba
Plinio Grijalba

Posted on • Updated on

Carga de archivos a cloud storage por medio de firebase en angular

Contenido

Introducción

En este post se muestra como realizar la carga de archivos a cloud storage a través de firebase.

Configurar proyecto de firebase

Para configurar el proyecto de firebase se debe realizar los siguientes pasos:

  1. Acceder a firebase console y crear un nuevo proyecto.
    Crear proyecto

  2. Habilitar storage en firebase.
    Para esto en el menú lateral se encuentra la opción de storage, al ingresar a dicha opción se da click en botón de comenzar y se siguen los pasos.
    Configurar storage
    Finalizado el proceso la ventana de storage se mostrara de la siguiente manera:
    storage
    Si se desea se puede ir a cloud storage y verificar la creación de bucket
    Cloud storage
    Por ultimo se va a la pestaña de Rules y se cambia la regla para que no solicite autenticarse para realizar la carga de los archivos.
    La cual queda de la siguiente manera:
    Cambiar regla

  3. Agregar app en firebase
    En la ventana principal del proyecto se encuentra la opción de agregar app, donde muestra diferentes plataformas a seleccionar en este caso se selecciona Web y se siguen los pasos.
    Agregar app en fireabse.

Crear proyecto de Angular

Si desea puede omitir este paso y clonar el repositorio https://github.com/plinio141/test-storage-angular.
Para crear y configurar el proyecto de Angular utilizar los siguientes comandos:

 ng new test-storage-angular
 cd test-storage-angular
 ng add @angular/material
 npm i -s @angular/flex-layout 

Posteriormente agregar al app.module lo siguiente

import {MatButtonModule} from '@angular/material/button';
import { FlexLayoutModule } from '@angular/flex-layout';

@NgModule({
  ...
  imports: [
    ...
    MatButtonModule,
    FlexLayoutModule
  ],
  ...
})

Conectar angular con firebase

Para esto seguir los siguientes pasos:

Tomar el Json que genero firebase al registrar la aplicación y agregarlo al archivo de environment en angular de la siguiente manera:

export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    authDomain: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    databaseURL: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    projectId: "XXXXXXXXXXXXXXXXXXXXX",
    storageBucket: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXXXXX",
    appId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  }
};

Instalar librería firebase y @angular/fire

 npm i --save firebase
 npm i @angular/fire --save

Importar AngularFireModule, AngularFireStorageModule y environment en el app.module

import { AngularFireModule } from '@angular/fire';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { environment } from '../environments/environment';

Agregar a los imports las siguientes lineas:

AngularFireModule.initializeApp(environment.firebaseConfig),
AngularFireStorageModule,

El app.module quedaría de la siguiente manera:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AngularFireModule } from '@angular/fire';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { MatButtonModule } from '@angular/material/button';
import { FlexLayoutModule } from '@angular/flex-layout';

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    MatButtonModule,
    FlexLayoutModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireStorageModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Ahora se crea una interfaz gráfica simple que contenga un input tipo file y un botón que tendrá el evento de carga del archivo en este caso se hará en el app.component.html

<section fxLayout="column" fxLayoutAlign="space-around center" fxFlex="100" fxLayoutGap="20px">
  <div class="label" fxFlex="30">Archivo</div>
  <div fxFlex="30"><input type="file" name="file"/></div>
  <div fxFlex="30">
    <button mat-flat-button color="primary">Cargar</button>
  </div>
</section>

Si desea puede agregar los siguientes estilos:

section {
  padding: 10px;
}
.label {
  font-size: 30px;
}

Ahora en app.component.ts se agrega una propiedad llamada file de tipo File y se crea una función que va capturar el evento de selección de una imagen.

  file: File;
onFileSelect(event) {
    if (event.target.files.length > 0) {
      this.file = event.target.files[0];
    }
  }

Se agrega al input file el evento change apuntando a la función anteriormente creada.

<div fxFlex="30"><input type="file" name="file" (change)="onFileSelect($event)"/></div>

En el app.component.ts importar el AngularFireStorage y agregar una nueva función que sera la encargada de cargar los archivos.

import { AngularFireStorage } from '@angular/fire/storage';
...
 constructor(
    private storage: AngularFireStorage
 ){}
...
uploadFile() {
    const filePath = this.file.name;
    // Crea una referencia de acceso
    const fileRef = this.storage.ref(filePath);
    fileRef.put(this.file);
  }

Agregar al botón Cargar el evento click para que cargue el archivo.

<button mat-flat-button color="primary" (click)="uploadFile()">Cargar</button>

Con esto ya se carga un archivo a cloud storage por medio de firebase, pero visualmente no se identifica que ya fue cargado el archivo así que se va crear un mensaje de carga completa. Para esto se añade en el html lo siguiente:

<div class="label" fxFlex="30" *ngIf="completed">Archivo cargado</div>

Además, en el app.component.ts se agrega una propiedad para mostrar y ocultar el mensaje de carga completa, de igual manera se debe modificar la función de uploadFile para actualizar la propiedad completed cuando se termine de cargar el archivo.

uploadFile() {
    this.completed = false;
    const filePath = this.file.name;
    // Crea una referencia de acceso
    const fileRef = this.storage.ref(filePath);
    fileRef.put(this.file).then(() => {
      this.completed = true;
    });
  }

Por ultimo se puede añadir una barra de carga. Para esto se va hacer uso del componente mat-progress-bar de angular material para lo cual se debe importar en el app.module.

...
import { MatProgressBarModule } from '@angular/material/progress-bar';
...
imports: [
    ...
    MatProgressBarModule,
    ...
  ],
...

Para usar el componente se añade al html de la siguiente manera:

<mat-progress-bar *ngIf="!completed" mode="determinate" value="uploadPercent | async"></mat-progress-bar>

En el app.component se añade la propiedad uploadPercent y se actualiza la función uploadFile para esto se debe importar finalize y Observable para un funcionamiento correcto.

import { finalize } from 'rxjs/operators';
import { Observable } from 'rxjs';
...
uploadFile() {
    this.completed = false;
    const filePath = this.file.name;
    const task = this.storage.upload(filePath, this.file);

    this.uploadPercent = task.percentageChanges();

    task.snapshotChanges().pipe(
      finalize(() => {
        this.completed = true;
      })
    )
    .subscribe();
  }

Los archivos quedan de la siguiente manera:
app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AngularFireModule } from '@angular/fire';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { MatButtonModule } from '@angular/material/button';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatProgressBarModule } from '@angular/material/progress-bar';

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    MatButtonModule,
    MatProgressBarModule,
    FlexLayoutModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireStorageModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  file: File;
  completed = false;
  uploadPercent: Observable<number>;

  constructor(
    private storage: AngularFireStorage
  ){}

  uploadFile() {
    this.completed = false;
    const filePath = this.file.name;
    const task = this.storage.upload(filePath, this.file);

    this.uploadPercent = task.percentageChanges();

    task.snapshotChanges().pipe(
      finalize(() => {
        this.completed = true;
      })
    )
    .subscribe();
  }

  onFileSelect(event) {
    if (event.target.files.length > 0) {
      this.file = event.target.files[0];
    }
  }

}

app.component.html

<section fxLayout="column" fxLayoutAlign="space-around center" fxFlex="100" fxLayoutGap="20px">
  <div class="label" fxFlex="30">Archivo</div>
  <div fxFlex="30"><input type="file" name="file" (change)="onFileSelect($event)"/></div>
  <div fxFlex="30">
    <button mat-flat-button color="primary" (click)="uploadFile()">Cargar</button>
  </div>
  <mat-progress-bar mode="determinate" [value]="uploadPercent | async"></mat-progress-bar>
  <div class="label" fxFlex="30" *ngIf="completed">Archivo cargado</div>
</section>

Este código se encuentra en https://github.com/plinio141/test-storage-angular

Discussion (0)