今回も
https://github.com/codediodeio/angular-firestarter/tree/master/src/app/uploads
を参考にしています
サンプルとして既にあるのですが
ファイルアップベースにfirebase functionを動かしたりしたいので
1から作って理解したいと思いやっています
- 前提として
- 1・クラスの作成
- 2・サービスの作成
- 3・コンポーネント
- 3-1・画像リスト
- 3-2・画像リストの詳細
- 3-3・応募フォーム
- 4・モジュール
- 5・app.module.tsとrouteの設定
- 6・結果
前提として
・アップロードにはauthの認証が必須にしています
チェックはしていないので、パーミッションエラーになります
・読み込み時のアニメーションももともと入っている
いれたい人はこのコンポーネントをいれる
https://github.com/codediodeio/angular-firestarter/tree/master/src/app/ui/loading-spinner
必須じゃないのでいらない人は削除する
1・クラスの作成
FirebaseoのDBに保存する形を入力する
ng g class uploads2/shared/upload
uploads2/shared/upload.tsができていると思うので下記を入力する
export class Upload { $key: string; file:File; name:string; url:string; progress:number; createdAt: Date = new Date(); constructor(file:File) { this.file = file; } }
Firebaseに入った状態は以下のとおりです
2・サービスの作成
フォームからアップロードされたり、削除されたりする時に
動作を記述する
ng g service uploads2/shared/upload
uploads2/shared/upload.service.tsができていると思うので下記を入力する
import { Injectable } from '@angular/core'; import { Upload } from './upload'; import { AngularFireDatabase, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2/database'; import * as firebase from 'firebase'; @Injectable() export class UploadService { constructor(private db: AngularFireDatabase) { } //firebaseのDBの保存先 private basePath:string = '/uploads'; uploads: FirebaseListObservable<upload[]>; getUploads(query={}) { this.uploads = this.db.list(this.basePath, { query: query }); return this.uploads } deleteUpload(upload: Upload) { //DBの部分を削除 this.deleteFileData(upload.$key) .then( () => { //成功したらバケットにある実ファイルを削除する this.deleteFileStorage(upload.name) }) .catch(error => console.log(error)) } // ファイルをアップして、その内容をDBに書き込む処理 // Executes the file uploading to firebase https://firebase.google.com/docs/storage/web/upload-files pushUpload(upload: Upload) { const storageRef = firebase.storage().ref(); const uploadTask = storageRef.child(`${this.basePath}/${upload.file.name}`).put(upload.file); uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot) => { // upload in progress(アップロード中の状態) let snap = snapshot as firebase.storage.UploadTaskSnapshot upload.progress = (snap.bytesTransferred / snap.totalBytes) * 100 }, (error) => { // upload failed console.log(error) }, () => { // upload success(アップロード成功時の処理) upload.url = uploadTask.snapshot.downloadURL //表示する時のURL upload.name = upload.file.name //ファイル名 this.saveFileData(upload) return undefined } ); } // Writes the file details to the realtime db private saveFileData(upload: Upload) { this.db.list(`${this.basePath}/`).push(upload); } // Writes the file details to the realtime db private deleteFileData(key: string) { return this.db.list(`${this.basePath}/`).remove(key); } // Firebase files must have unique names in their respective storage dir // So the name serves as a unique key private deleteFileStorage(name:string) { const storageRef = firebase.storage().ref(); storageRef.child(`${this.basePath}/${name}`).delete() } }
3・コンポーネント
ng g componentのコマンドでもつくれるのですが
src/app/app.module.tsが更新されてしまうのと
いらないSCSSができてしまうので手動で対応
# プロジェクトの直下にいる状態とする mkdir src/app/uploads2/upload-detail mkdir src/app/uploads2/upload-form mkdir src/app/uploads2/uploads-list
3-1・画像リスト
# プロジェクトの直下にいる状態とする
$ vi src/app/uploads2/uploads-list/uploads-list.component.ts
import { Component, OnInit } from '@angular/core'; import { FirebaseListObservable } from 'angularfire2/database'; import { UploadService } from '../shared/upload.service'; import { Upload } from '../shared/upload'; @Component({ selector: 'uploads-list', template: `現在のアップされているリスト <div *ngfor="let upload of uploads | async"> <upload-detail [upload]="upload"></upload-detail> </div> <!-- loading animations ないなら削除でOK --> <loading-spinner *ngif="showSpinner"></loading-spinner> <hr> アップする時のフォーム <upload-form></upload-form>` }) export class UploadsListComponent implements OnInit { uploads: FirebaseListObservable<upload[]>; //loading animations用 showSpinner = true; constructor(private upSvc: UploadService) { } ngOnInit() { //タイムスタンプが新しい順で取得:10件取得 this.uploads = this.upSvc.getUploads({limitToLast: 10}) //load end animations off リストが読み込んだらアニメーションをオフにする this.uploads.subscribe(() => this.showSpinner = false) } }
3-2・画像リストの詳細
リストの下記のループで回っている部分の詳細になる
<div *ngfor="let upload of uploads | async"> <upload-detail [upload]="upload"></upload-detail> </div>
# プロジェクトの直下にいる状態とする
$ vi src/app/uploads2/upload-detail/upload-detail.component.ts
import { Component, OnInit, Input } from '@angular/core'; import { UploadService } from '../shared/upload.service'; import { Upload } from '../shared/upload'; @Component({ selector: 'upload-detail', template: ` <strong>{{upload.name}}</strong> <button (click)="deleteUpload(upload)" class="button is-danger is-small">Delete</button><br> ` }) export class UploadDetailComponent implements OnInit { @Input() upload: Upload; constructor(private upSvc: UploadService) { } ngOnInit() { } //削除ボタンを押された時の処理(確認画面もなく削除される・・・あとで修正) deleteUpload(upload) { this.upSvc.deleteUpload(this.upload) } }
3-3・応募フォーム
$ vi src/app/uploads2/upload-form/upload-form.component.ts
import { Component, OnInit } from '@angular/core'; import { UploadService } from '../shared/upload.service'; import { Upload } from '../shared/upload'; import * as _ from "lodash"; @Component({ selector: 'upload-form', templateUrl: './upload-form.component.html' }) export class UploadFormComponent implements OnInit { selectedFiles: FileList; currentUpload: Upload; constructor(private upSvc: UploadService) { } ngOnInit() { } detectFiles(event) { this.selectedFiles = event.target.files; } //単一ファイルアップで仕様 uploadSingle() { let file = this.selectedFiles.item(0) this.currentUpload = new Upload(file); this.upSvc.pushUpload(this.currentUpload) } //複数ファイルアップで仕様 uploadMulti() { let files = this.selectedFiles if (_.isEmpty(files)) return; let filesIndex = _.range(files.length) _.each(filesIndex, (idx) => { this.currentUpload = new Upload(files[idx]); this.upSvc.pushUpload(this.currentUpload)} ) } }
$ vi src/app/uploads2/upload-form/upload-form.component.html
<div *ngif="currentUpload"> <progress class="progress is-success" min="1" max="100" value="{{ currentUpload?.progress }}"></progress> Progress: {{currentUpload?.name}} | {{currentUpload?.progress}}% Complete </div> <div class="box"> アップにはログインが必要です。ログインチェックをしていません。アップした場合は パーミッションエラーが発生します。 <h3>Single File Upload</h3> <label> <input type="file" class="button" (change)="detectFiles($event)"> </label> <button class="button is-primary" [disabled]="!selectedFiles" (click)="uploadSingle()"> アップを開始する </button> <hr> <h3>複数(Multiple) File Upload</h3> <label> <input type="file" class="button" (change)="detectFiles($event)" multiple> </label> <button class="button is-primary" [disabled]="!selectedFiles" (click)="uploadMulti()"> アップを開始する </button> </div>
4・モジュール
app.module.tsで読み込ませるために記述する
ng g module uploads2/shared/upload
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '../../shared/shared.module'; import { AngularFireDatabaseModule } from 'angularfire2/database'; import { UploadService } from './upload.service'; import { UploadFormComponent } from '../upload-form/upload-form.component'; import { UploadsListComponent } from '../uploads-list/uploads-list.component'; import { UploadDetailComponent } from '../upload-detail/upload-detail.component'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: '', component: UploadsListComponent } ]; @NgModule({ imports: [ CommonModule, SharedModule, AngularFireDatabaseModule, RouterModule.forChild(routes) ], declarations: [ UploadFormComponent, UploadsListComponent, UploadDetailComponent, ], providers: [ UploadService ] }) export class UploadModule2 { }
5・app.module.tsとrouteの設定
6・結果
https://angular-study-chat.firebaseapp.com/uploads2
ファイルアップができて大まかな流れを掴んだ
これをベースにサムネイル作成とか写真の情報を取得とかやりたい>>