├── .editorconfig
├── .gitignore
├── README.md
├── angular-cli.json
├── debug.log
├── doc
└── images
│ ├── list.png
│ └── upload.png
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.json
├── karma.conf.js
├── package.json
├── protractor.conf.js
├── src
├── app
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.config.ts
│ ├── app.module.ts
│ ├── directives
│ │ ├── file-item.ts
│ │ ├── ng-drop-files.directive.spec.ts
│ │ └── ng-drop-files.directive.ts
│ ├── index.ts
│ ├── list-images
│ │ ├── list-images.component.css
│ │ ├── list-images.component.html
│ │ ├── list-images.component.spec.ts
│ │ └── list-images.component.ts
│ ├── services
│ │ ├── upload-images.service.spec.ts
│ │ └── upload-images.service.ts
│ └── upload-images
│ │ ├── upload-images.component.css
│ │ ├── upload-images.component.html
│ │ ├── upload-images.component.spec.ts
│ │ └── upload-images.component.ts
├── assets
│ ├── .gitkeep
│ └── images
│ │ └── drop-images.png
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
├── tsconfig.json
└── typings.d.ts
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /bower_components
10 |
11 | # IDEs and editors
12 | /.idea
13 | /.vscode
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 |
20 | # misc
21 | /.sass-cache
22 | /connect.lock
23 | /coverage/*
24 | /libpeerconnection.log
25 | npm-debug.log
26 | testem.log
27 | /typings
28 |
29 | # e2e
30 | /e2e/*.js
31 | /e2e/*.map
32 |
33 | #System Files
34 | .DS_Store
35 | Thumbs.db
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UploadFirebase - Project description
2 |
3 | This project is an Angular2 demo to show how to use Firebase Storage to upload images in Firebase and how to use Firebase Realtime Database to see in real time the files that are uploaded.
4 |
5 | ## Screenshots
6 |
7 | **Upload files:**
8 |
9 | [](doc/images/upload.png)
10 |
11 | **List files:**
12 |
13 | [](doc/images/list.png)
14 |
15 |
16 | ## Download and run the application
17 | - If you have not installed angular-cli, you have to install it: `npm install angular-cli --save`
18 | - Clone this repo and enter inside the folder
19 | - run `npm install`
20 | - run `ng serve`
21 |
22 | ## Live demo
23 |
24 | You can see this project running in **Firebase Hosting** in this URL: [https://upload-582a4.firebaseapp.com/](https://upload-582a4.firebaseapp.com/)
25 |
26 | If you want to see working the **Firebase Realtime Database** then you can open two different browsers, one with the **List Images** page and the other one with the **Upload Images** page. Then you will see that when you upload images they will be displayed automatically in the **list images** page.
27 |
--------------------------------------------------------------------------------
/angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "project": {
3 | "version": "1.0.0-beta.22-1",
4 | "name": "upload-firebase"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "test": "test.ts",
17 | "tsconfig": "tsconfig.json",
18 | "prefix": "app",
19 | "mobile": false,
20 | "styles": [
21 | "styles.css"
22 | ],
23 | "scripts": [],
24 | "environments": {
25 | "source": "environments/environment.ts",
26 | "dev": "environments/environment.ts",
27 | "prod": "environments/environment.prod.ts"
28 | }
29 | }
30 | ],
31 | "addons": [],
32 | "packages": [],
33 | "e2e": {
34 | "protractor": {
35 | "config": "./protractor.conf.js"
36 | }
37 | },
38 | "test": {
39 | "karma": {
40 | "config": "./karma.conf.js"
41 | }
42 | },
43 | "defaults": {
44 | "styleExt": "css",
45 | "prefixInterfaces": false,
46 | "inline": {
47 | "style": false,
48 | "template": false
49 | },
50 | "spec": {
51 | "class": false,
52 | "component": true,
53 | "directive": true,
54 | "module": false,
55 | "pipe": true,
56 | "service": true
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/debug.log:
--------------------------------------------------------------------------------
1 | [1216/162427:ERROR:tcp_listen_socket.cc(76)] Could not bind socket to 127.0.0.1:6004
2 | [1216/162427:ERROR:node_debugger.cc(86)] Cannot start debugger server
3 |
--------------------------------------------------------------------------------
/doc/images/list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlmonteagudo/upload-firebase/dfd84946bc0cb8d956f70dea398011ea699691c7/doc/images/list.png
--------------------------------------------------------------------------------
/doc/images/upload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlmonteagudo/upload-firebase/dfd84946bc0cb8d956f70dea398011ea699691c7/doc/images/upload.png
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { UploadFirebasePage } from './app.po';
2 |
3 | describe('upload-firebase App', function() {
4 | let page: UploadFirebasePage;
5 |
6 | beforeEach(() => {
7 | page = new UploadFirebasePage();
8 | });
9 |
10 | it('should display message saying app works', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('app works!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, element, by } from 'protractor';
2 |
3 | export class UploadFirebasePage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "module": "commonjs",
8 | "moduleResolution": "node",
9 | "outDir": "../dist/out-tsc-e2e",
10 | "sourceMap": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "../node_modules/@types"
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', 'angular-cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-remap-istanbul'),
12 | require('angular-cli/plugins/karma')
13 | ],
14 | files: [
15 | { pattern: './src/test.ts', watched: false }
16 | ],
17 | preprocessors: {
18 | './src/test.ts': ['angular-cli']
19 | },
20 | mime: {
21 | 'text/x-typescript': ['ts','tsx']
22 | },
23 | remapIstanbulReporter: {
24 | reports: {
25 | html: 'coverage',
26 | lcovonly: './coverage/coverage.lcov'
27 | }
28 | },
29 | angularCli: {
30 | config: './angular-cli.json',
31 | environment: 'dev'
32 | },
33 | reporters: config.angularCli && config.angularCli.codeCoverage
34 | ? ['progress', 'karma-remap-istanbul']
35 | : ['progress'],
36 | port: 9876,
37 | colors: true,
38 | logLevel: config.LOG_INFO,
39 | autoWatch: true,
40 | browsers: ['Chrome'],
41 | singleRun: false
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "upload-firebase",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "angular-cli": {},
6 | "scripts": {
7 | "start": "ng serve",
8 | "lint": "tslint \"src/**/*.ts\"",
9 | "test": "ng test",
10 | "pree2e": "webdriver-manager update",
11 | "e2e": "protractor"
12 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/common": "2.2.3",
16 | "@angular/compiler": "2.2.3",
17 | "@angular/core": "2.2.3",
18 | "@angular/forms": "2.2.3",
19 | "@angular/http": "2.2.3",
20 | "@angular/platform-browser": "2.2.3",
21 | "@angular/platform-browser-dynamic": "2.2.3",
22 | "@angular/router": "3.2.3",
23 | "@types/lodash": "^4.14.43",
24 | "angularfire2": "^2.0.0-beta.6",
25 | "core-js": "^2.4.1",
26 | "firebase": "^3.6.4",
27 | "lodash": "^4.17.2",
28 | "rxjs": "5.0.0-beta.12",
29 | "ts-helpers": "^1.1.1",
30 | "zone.js": "^0.6.23"
31 | },
32 | "devDependencies": {
33 | "@angular/compiler-cli": "2.2.3",
34 | "@types/jasmine": "2.5.38",
35 | "@types/node": "^6.0.42",
36 | "angular-cli": "1.0.0-beta.22-1",
37 | "codelyzer": "~2.0.0-beta.1",
38 | "jasmine-core": "2.5.2",
39 | "jasmine-spec-reporter": "2.5.0",
40 | "karma": "1.2.0",
41 | "karma-chrome-launcher": "^2.0.0",
42 | "karma-cli": "^1.0.1",
43 | "karma-jasmine": "^1.0.2",
44 | "karma-remap-istanbul": "^0.2.1",
45 | "protractor": "4.0.9",
46 | "ts-node": "1.2.1",
47 | "tslint": "^4.0.2",
48 | "typescript": "~2.0.3",
49 | "webdriver-manager": "10.2.5"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/docs/referenceConf.js
3 |
4 | /*global jasmine */
5 | var SpecReporter = require('jasmine-spec-reporter');
6 |
7 | exports.config = {
8 | allScriptsTimeout: 11000,
9 | specs: [
10 | './e2e/**/*.e2e-spec.ts'
11 | ],
12 | capabilities: {
13 | 'browserName': 'chrome'
14 | },
15 | directConnect: true,
16 | baseUrl: 'http://localhost:4200/',
17 | framework: 'jasmine',
18 | jasmineNodeOpts: {
19 | showColors: true,
20 | defaultTimeoutInterval: 30000,
21 | print: function() {}
22 | },
23 | useAllAngular2AppRoots: true,
24 | beforeLaunch: function() {
25 | require('ts-node').register({
26 | project: 'e2e'
27 | });
28 | },
29 | onPrepare: function() {
30 | jasmine.getEnv().addReporter(new SpecReporter());
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | .content { margin-top: 70px; }
2 | .active { font-weight: bolder; color: white }
3 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { TestBed, async } from '@angular/core/testing';
4 | import { AppComponent } from './app.component';
5 |
6 | describe('AppComponent', () => {
7 | beforeEach(() => {
8 | TestBed.configureTestingModule({
9 | declarations: [
10 | AppComponent
11 | ],
12 | });
13 | TestBed.compileComponents();
14 | });
15 |
16 | it('should create the app', async(() => {
17 | let fixture = TestBed.createComponent(AppComponent);
18 | let app = fixture.debugElement.componentInstance;
19 | expect(app).toBeTruthy();
20 | }));
21 |
22 | it(`should have as title 'app works!'`, async(() => {
23 | let fixture = TestBed.createComponent(AppComponent);
24 | let app = fixture.debugElement.componentInstance;
25 | expect(app.title).toEqual('app works!');
26 | }));
27 |
28 | it('should render title in a h1 tag', async(() => {
29 | let fixture = TestBed.createComponent(AppComponent);
30 | fixture.detectChanges();
31 | let compiled = fixture.debugElement.nativeElement;
32 | expect(compiled.querySelector('h1').textContent).toContain('app works!');
33 | }));
34 | });
35 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.css']
7 | })
8 | export class AppComponent {
9 | title = 'app works!';
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/app.config.ts:
--------------------------------------------------------------------------------
1 |
2 | export const firebaseConfig = {
3 | apiKey: "AIzaSyCw8f-asAPD5e3OUqI6HhGKU9kux7Qe1Ts",
4 | authDomain: "upload-582a4.firebaseapp.com",
5 | databaseURL: "https://upload-582a4.firebaseio.com",
6 | storageBucket: "upload-582a4.appspot.com",
7 | messagingSenderId: "285936178446"
8 | };
9 |
10 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpModule } from '@angular/http';
5 | import { RouterModule, Routes } from '@angular/router';
6 | import { AngularFireModule } from 'angularfire2';
7 |
8 | import * as config from './app.config';
9 | import { AppComponent } from './app.component';
10 | import { UploadImagesComponent } from './upload-images/upload-images.component';
11 | import { ListImagesComponent } from './list-images/list-images.component';
12 | import { NgDropFilesDirective } from './directives/ng-drop-files.directive';
13 | import { UploadImagesService } from './services/upload-images.service';
14 |
15 | const appRoutes: Routes = [
16 | { path: '', component: ListImagesComponent },
17 | { path: 'list', component: ListImagesComponent },
18 | { path: 'upload', component: UploadImagesComponent },
19 | { path: '**', component: ListImagesComponent }
20 | ];
21 |
22 | @NgModule({
23 | declarations: [
24 | AppComponent,
25 | UploadImagesComponent,
26 | ListImagesComponent,
27 | NgDropFilesDirective,
28 | NgDropFilesDirective
29 | ],
30 | imports: [
31 | BrowserModule,
32 | FormsModule,
33 | HttpModule,
34 | RouterModule.forRoot(appRoutes),
35 | AngularFireModule.initializeApp(config.firebaseConfig)
36 | ],
37 | providers: [ UploadImagesService ],
38 | bootstrap: [AppComponent]
39 | })
40 | export class AppModule { }
41 |
--------------------------------------------------------------------------------
/src/app/directives/file-item.ts:
--------------------------------------------------------------------------------
1 | export class FileItem {
2 |
3 | public file:File;
4 | public url:string = '';
5 | public isUploading:boolean = false;
6 | public progress:number = 0;
7 |
8 | public constructor(file:File) {
9 | this.file = file;
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/directives/ng-drop-files.directive.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { TestBed, async } from '@angular/core/testing';
4 | import { NgDropFilesDirective } from './ng-drop-files.directive';
5 |
6 | describe('NgDropFilesDirective', () => {
7 | it('should create an instance', () => {
8 | let directive = new NgDropFilesDirective();
9 | expect(directive).toBeTruthy();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/app/directives/ng-drop-files.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, EventEmitter, ElementRef, HostListener, Input, Output } from '@angular/core';
2 | import { FileItem } from './file-item';
3 | import * as _ from 'lodash';
4 |
5 | @Directive({
6 | selector: '[NgDropFiles]'
7 | })
8 | export class NgDropFilesDirective {
9 |
10 | @Input() public files:Array = [];
11 | @Output() public fileOver:EventEmitter = new EventEmitter();
12 | @Output() public onFileDrop:EventEmitter = new EventEmitter();
13 |
14 | private element:ElementRef;
15 |
16 | public constructor(element:ElementRef) {
17 | this.element = element;
18 | }
19 |
20 |
21 | @HostListener('drop', ['$event'])
22 | public onDrop(event:any):void {
23 | let transfer = this._getTransfer(event);
24 | if (!transfer) return;
25 |
26 | this._preventAndStop(event);
27 | this._addFiles(transfer.files);
28 | this.fileOver.emit(false);
29 | this.onFileDrop.emit(this.files);
30 | }
31 |
32 | @HostListener('draenter', ['$event'])
33 | public onDragEnter(event:any):void {
34 | this._preventAndStop(event);
35 | this.fileOver.emit(true);
36 | }
37 |
38 | @HostListener('dragover', ['$event'])
39 | public onDragOver(event:any):void {
40 | let transfer = this._getTransfer(event);
41 |
42 | transfer.dropEffect = 'copy';
43 | this._preventAndStop(event);
44 | this.fileOver.emit(true);
45 | }
46 |
47 |
48 | @HostListener('dragleave', ['$event'])
49 | public onDragLeave(event:any):any {
50 | this._preventAndStop(event);
51 | this.fileOver.emit(false);
52 | }
53 |
54 |
55 |
56 | private _getTransfer(event:any):any {
57 | return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer;
58 | }
59 |
60 | private _preventAndStop(event:any):any {
61 | event.preventDefault();
62 | event.stopPropagation();
63 | }
64 |
65 | private _addFiles(files: FileList):void {
66 | _.each(files, (file) => { if (this._fileCanBeAdded(file)) this.files.push(new FileItem(file)) });
67 | }
68 |
69 | private _fileCanBeAdded(file:File):boolean {
70 | return (!this._fileIsAlreadyDropped(file) && this._fileTypeIsImage(file.type));
71 | }
72 |
73 | private _fileIsAlreadyDropped(file:File):boolean {
74 | return _.filter(this.files, _.iteratee(['name', file.name])).length > 0;
75 | }
76 |
77 | private _fileTypeIsImage(fileType:string):boolean {
78 | return (fileType == ''? false: fileType.startsWith('image'));
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/app/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app.component';
2 | export * from './app.module';
3 |
--------------------------------------------------------------------------------
/src/app/list-images/list-images.component.css:
--------------------------------------------------------------------------------
1 | .masonry { /* Masonry container */
2 | column-count: 4;
3 | column-gap: 1em;
4 | }
5 |
6 | .item { /* Masonry bricks or child elements */
7 | background-color: #fff;
8 | display: inline-block;
9 | margin: 0 0 1em;
10 | width: 100%;
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/list-images/list-images.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
![]()
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/app/list-images/list-images.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { ListImagesComponent } from './list-images.component';
7 |
8 | describe('ListImagesComponent', () => {
9 | let component: ListImagesComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ ListImagesComponent ]
15 | })
16 | .compileComponents();
17 | }));
18 |
19 | beforeEach(() => {
20 | fixture = TestBed.createComponent(ListImagesComponent);
21 | component = fixture.componentInstance;
22 | fixture.detectChanges();
23 | });
24 |
25 | it('should create', () => {
26 | expect(component).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/list-images/list-images.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { FirebaseListObservable } from 'angularfire2';
3 | import { UploadImagesService } from '../services/upload-images.service';
4 |
5 |
6 | @Component({
7 | selector: 'app-list-images',
8 | templateUrl: './list-images.component.html',
9 | styleUrls: ['./list-images.component.css']
10 | })
11 | export class ListImagesComponent {
12 |
13 | private NUMBER_OF_IMAGES: number = 10;
14 | images: FirebaseListObservable;
15 |
16 | constructor(public uploadImagesService: UploadImagesService) {
17 | this.images = uploadImagesService.listLastImages(this.NUMBER_OF_IMAGES);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/services/upload-images.service.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { TestBed, async, inject } from '@angular/core/testing';
4 | import { UploadImagesService } from './upload-images.service';
5 |
6 | describe('UploadImagesServiceService', () => {
7 | beforeEach(() => {
8 | TestBed.configureTestingModule({
9 | providers: [UploadImagesService]
10 | });
11 | });
12 |
13 | it('should ...', inject([UploadImagesService], (service: UploadImagesService) => {
14 | expect(service).toBeTruthy();
15 | }));
16 | });
17 |
--------------------------------------------------------------------------------
/src/app/services/upload-images.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { AngularFire, FirebaseListObservable } from 'angularfire2';
3 | import { FileItem } from '../directives/file-item';
4 | import * as firebase from 'firebase';
5 | import * as _ from 'lodash';
6 |
7 | @Injectable()
8 | export class UploadImagesService {
9 |
10 | private IMAGES_FOLDER: string = 'images';
11 |
12 | constructor(public af: AngularFire) { }
13 |
14 | listLastImages(numberOfImages: number): FirebaseListObservable{
15 | return this.af.database.list(`/${this.IMAGES_FOLDER}`, {
16 | query: {
17 | limitToLast: numberOfImages
18 | }
19 | });
20 | }
21 |
22 | uploadImagesToFirebase(files: Array) {
23 | let storageRef = firebase.storage().ref();
24 |
25 | _.each(files, (item:FileItem) => {
26 |
27 | item.isUploading = true;
28 | let uploadTask: firebase.storage.UploadTask = storageRef.child(`${this.IMAGES_FOLDER}/${item.file.name}`).put(item.file);
29 |
30 | uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED,
31 | (snapshot) => item.progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100,
32 | (error) => {},
33 | () => {
34 | item.url = uploadTask.snapshot.downloadURL;
35 | item.isUploading = false;
36 | this.saveImage({ name: item.file.name, url: item.url });
37 | }
38 | );
39 |
40 | });
41 |
42 | }
43 |
44 | private saveImage(image:any) {
45 | this.af.database.list(`/${this.IMAGES_FOLDER}`).push(image);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/upload-images/upload-images.component.css:
--------------------------------------------------------------------------------
1 | .drop-zone { border: dotted 3px lightgray; opacity: 0.5; color: #666; text-align: center }
2 | .file-over { border: dotted 3px dodgerblue; }
3 | .table { margin-top: 30px; }
4 | .text-green { color: green }
5 |
--------------------------------------------------------------------------------
/src/app/upload-images/upload-images.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Select files
4 |
5 |
10 |
Drop your files here
11 |

12 |
13 |
14 |
15 |
16 |
17 |
18 |
Upload Files
19 |
20 |
23 |
26 |
27 |
0">
28 |
29 |
30 | Name |
31 | Size |
32 | Progress |
33 |
34 |
35 |
36 |
37 | {{ item?.file?.name }} |
38 | {{ item?.file?.size / 1024 / 1024 | number:'.2' }} MB |
39 |
40 |
43 | |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/app/upload-images/upload-images.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { UploadImagesComponent } from './upload-images.component';
7 |
8 | describe('UploadImagesComponent', () => {
9 | let component: UploadImagesComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ UploadImagesComponent ]
15 | })
16 | .compileComponents();
17 | }));
18 |
19 | beforeEach(() => {
20 | fixture = TestBed.createComponent(UploadImagesComponent);
21 | component = fixture.componentInstance;
22 | fixture.detectChanges();
23 | });
24 |
25 | it('should create', () => {
26 | expect(component).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/upload-images/upload-images.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { FileItem } from '../directives/file-item';
3 | import { UploadImagesService } from '../services/upload-images.service';
4 |
5 | @Component({
6 | selector: 'app-upload-images',
7 | templateUrl: './upload-images.component.html',
8 | styleUrls: ['./upload-images.component.css']
9 | })
10 | export class UploadImagesComponent {
11 |
12 | isDropZoneOver:boolean = false;
13 | isEnabledUpload: boolean = true;
14 | files: Array = [];
15 |
16 | constructor(public uploadImagesService: UploadImagesService) {
17 | }
18 |
19 | public fileOverDropZone(e:any):void {
20 | this.isDropZoneOver = e;
21 | }
22 |
23 | uploadImagesToFirebase() {
24 | this.isEnabledUpload = false;
25 | this.uploadImagesService.uploadImagesToFirebase(this.files);
26 | }
27 |
28 | clearFiles() {
29 | this.files = [];
30 | this.isEnabledUpload = true;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlmonteagudo/upload-firebase/dfd84946bc0cb8d956f70dea398011ea699691c7/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/assets/images/drop-images.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlmonteagudo/upload-firebase/dfd84946bc0cb8d956f70dea398011ea699691c7/src/assets/images/drop-images.png
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jlmonteagudo/upload-firebase/dfd84946bc0cb8d956f70dea398011ea699691c7/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UploadFirebase
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import './polyfills.ts';
2 |
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 | import { enableProdMode } from '@angular/core';
5 | import { environment } from './environments/environment';
6 | import { AppModule } from './app/';
7 |
8 | if (environment.production) {
9 | enableProdMode();
10 | }
11 |
12 | platformBrowserDynamic().bootstrapModule(AppModule);
13 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | // This file includes polyfills needed by Angular 2 and is loaded before
2 | // the app. You can add your own extra polyfills to this file.
3 | import 'core-js/es6/symbol';
4 | import 'core-js/es6/object';
5 | import 'core-js/es6/function';
6 | import 'core-js/es6/parse-int';
7 | import 'core-js/es6/parse-float';
8 | import 'core-js/es6/number';
9 | import 'core-js/es6/math';
10 | import 'core-js/es6/string';
11 | import 'core-js/es6/date';
12 | import 'core-js/es6/array';
13 | import 'core-js/es6/regexp';
14 | import 'core-js/es6/map';
15 | import 'core-js/es6/set';
16 | import 'core-js/es6/reflect';
17 |
18 | import 'core-js/es7/reflect';
19 | import 'zone.js/dist/zone';
20 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | import './polyfills.ts';
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare var __karma__: any;
17 | declare var require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | let context = require.context('./', true, /\.spec\.ts/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "",
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "lib": ["es6", "dom"],
8 | "mapRoot": "./",
9 | "module": "es6",
10 | "moduleResolution": "node",
11 | "outDir": "../dist/out-tsc",
12 | "sourceMap": true,
13 | "target": "es5",
14 | "typeRoots": [
15 | "../node_modules/@types"
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | // Typings reference file, you can add your own global typings here
2 | // https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html
3 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "class-name": true,
7 | "comment-format": [
8 | true,
9 | "check-space"
10 | ],
11 | "curly": true,
12 | "eofline": true,
13 | "forin": true,
14 | "indent": [
15 | true,
16 | "spaces"
17 | ],
18 | "label-position": true,
19 | "max-line-length": [
20 | true,
21 | 140
22 | ],
23 | "member-access": false,
24 | "member-ordering": [
25 | true,
26 | "static-before-instance",
27 | "variables-before-functions"
28 | ],
29 | "no-arg": true,
30 | "no-bitwise": true,
31 | "no-console": [
32 | true,
33 | "debug",
34 | "info",
35 | "time",
36 | "timeEnd",
37 | "trace"
38 | ],
39 | "no-construct": true,
40 | "no-debugger": true,
41 | "no-duplicate-variable": true,
42 | "no-empty": false,
43 | "no-eval": true,
44 | "no-inferrable-types": true,
45 | "no-shadowed-variable": true,
46 | "no-string-literal": false,
47 | "no-switch-case-fall-through": true,
48 | "no-trailing-whitespace": true,
49 | "no-unused-expression": true,
50 | "no-use-before-declare": true,
51 | "no-var-keyword": true,
52 | "object-literal-sort-keys": false,
53 | "one-line": [
54 | true,
55 | "check-open-brace",
56 | "check-catch",
57 | "check-else",
58 | "check-whitespace"
59 | ],
60 | "quotemark": [
61 | true,
62 | "single"
63 | ],
64 | "radix": true,
65 | "semicolon": [
66 | "always"
67 | ],
68 | "triple-equals": [
69 | true,
70 | "allow-null-check"
71 | ],
72 | "typedef-whitespace": [
73 | true,
74 | {
75 | "call-signature": "nospace",
76 | "index-signature": "nospace",
77 | "parameter": "nospace",
78 | "property-declaration": "nospace",
79 | "variable-declaration": "nospace"
80 | }
81 | ],
82 | "variable-name": false,
83 | "whitespace": [
84 | true,
85 | "check-branch",
86 | "check-decl",
87 | "check-operator",
88 | "check-separator",
89 | "check-type"
90 | ],
91 |
92 | "directive-selector": [true, "attribute", "app", "camelCase"],
93 | "component-selector": [true, "element", "app", "kebab-case"],
94 | "use-input-property-decorator": true,
95 | "use-output-property-decorator": true,
96 | "use-host-property-decorator": true,
97 | "no-input-rename": true,
98 | "no-output-rename": true,
99 | "use-life-cycle-interface": true,
100 | "use-pipe-transform-interface": true,
101 | "component-class-suffix": true,
102 | "directive-class-suffix": true,
103 | "no-access-missing-member": true,
104 | "templates-use-public": true,
105 | "invoke-injectable": true
106 | }
107 | }
108 |
--------------------------------------------------------------------------------