├── .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 | [![Upload Files](doc/images/upload.png)](doc/images/upload.png) 10 | 11 | **List files:** 12 | 13 | [![List Files](doc/images/list.png)](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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 |
NameSizeProgress
{{ item?.file?.name }}{{ item?.file?.size / 1024 / 1024 | number:'.2' }} MB 40 |
41 |
42 |
43 |
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 | --------------------------------------------------------------------------------