[AngularFire] Angular File Uploads to Firebase Storage with Angular control value accessor

The upload class will be used in the service layer. Notice it has a constructor for file attribute, which has a type of File. This will allows us to initialize new uploads with a JavaScript File object. You will see why this is important in the next step.

export class Upload {
$key: string;
file:File;
name:string;
url:string;
progress:number;
createdAt: Date = new Date();
constructor(file:File) {
this.file = file;
}
}

Then build the upload service, which can inject to component:

import { Injectable } from '@angular/core';
import {Subject} from 'rxjs/Subject';
import {MatSnackBar} from '@angular/material'; import * as firebase from 'firebase';
import UploadTaskSnapshot = firebase.storage.UploadTaskSnapshot;
import {Upload} from './upload'; @Injectable()
export class UploadService { uploading$ = new Subject<number>();
completed$ = new Subject<Upload>(); constructor(
private snackBar: MatSnackBar
) { } uploadFile(upload: Upload, folder: string) { // Create a storage ref
const storageRef = firebase.storage().ref();
const uploadTask = storageRef.child(`${folder}/${upload.file.name}`).put(upload.file);
// Upload file
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED,
(snapshot: UploadTaskSnapshot) => {
upload.progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
this.uploading$.next(upload.progress);
},
(err) => {
this.snackBar.open(err.message, 'OK', {
duration: 3000,
});
},
() => {
upload.url = uploadTask.snapshot.downloadURL;
upload.name = upload.file.name;
this.completed$.next(upload);
this.uploading$.next(null);
});
} deleteUpload(name: string, folder: string) {
const storageRef = firebase.storage().ref();
storageRef.child(`${folder}/${name}`).delete();
this.completed$.next();
}
}

Component:

import {ChangeDetectionStrategy, Component, forwardRef, Input} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import {UploadService} from '../../services/upload.service';
import {Upload} from '../../services/upload';
import {Observable} from 'rxjs/Observable'; export const TYPE_CONTROL_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => ImageUploaderComponent)
}; @Component({
selector: 'image-uploader',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [TYPE_CONTROL_ACCESSOR],
templateUrl: './image-uploader.component.html',
styleUrls: ['./image-uploader.component.scss']
})
export class ImageUploaderComponent implements ControlValueAccessor { @Input() img; private onTouch: Function;
private onModelChange: Function;
private value: string; file: Upload;
currentUpload: Upload;
progress$: Observable<number>; constructor(private uploadService: UploadService) {
this.progress$ = this.uploadService.uploading$;
this.uploadService.completed$.subscribe((upload) => { if (upload) {
this.setSelected(upload.url);
this.currentUpload = upload;
} else {
this.setSelected('');
this.currentUpload = null;
}
});
} onChange($event) {
const file = $event.target.files[0];
this.file = new Upload(file);
this.uploadService.uploadFile(this.file, 'icons');
} writeValue(value: any): void {
this.value = value;
} registerOnChange(fn: Function): void {
this.onModelChange = fn;
} registerOnTouched(fn: Function): void {
this.onTouch = fn;
} setSelected(value: string): void {
this.value = value;
this.onModelChange(value);
this.onTouch();
} clear() {
if (this.file) {
this.uploadService.deleteUpload(this.file.name, 'icons');
this.setSelected('');
}
}
}

Template:

    <div *ngIf="progress$ | async as p">
<mat-progress-bar mode="determinate" [value]="p"></mat-progress-bar>
</div>
<mat-card-subtitle>
Select / upload icon
</mat-card-subtitle> <mat-card-content fxLayout="column">
<div fxLayout="row" fxLayoutAlign="space-around">
<div
*ngIf="currentUpload"
class="image-container"
fxFlex="30%">
<img [src]="currentUpload?.url || ''" [alt]="currentUpload?.name || ''">
</div>
</div>
上一篇:二识angularJS


下一篇:MyEclipse 2016 CI 4新增BootStrap模板