티스토리 뷰
Ionic Framework & Firebase를 기용하여 현재까지 구현된 기능은 사용자 등록, 로그인, 사용자 리스트 출력 기능 입니다. 이번 포스팅에서는 Cordova의 Camera Plugin을 설치하여 갤러리에서 사진을 선택하고 Firebase Storage에 업로드하여 페이지에 출력해보는 임의의 사용자 프로필 화면을 구현해 보도록 하겠습니다.
Camera Plugin 추가
(1) Plugin, Package 설치
사진 찍기 및 업로드 기능을 구현하기 위해 Camera Plugin과 Package를 추가해야 합니다.
$ionic plugin add cordova-plugin-camera --save
$npm install --save @ionic-native/camera
(2) 프로젝트에 Module 추가
최상위 app.module.ts에 설치한 Camera를 import하고 providers에 등록해줍니다. 그리고 이후 생성할 ProfilePage 또한 import 합니다.
src/app/app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | //Angular Module import { BrowserModule } from '@angular/platform-browser'; import { HttpModule } from '@angular/http'; import { ErrorHandler, NgModule } from '@angular/core'; //Ionic Module import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular'; import { StatusBar } from '@ionic-native/status-bar'; import { SplashScreen } from '@ionic-native/splash-screen'; import { Camera } from '@ionic-native/camera'; //Firebase Module import { AngularFireAuthModule } from 'angularfire2/auth'; import { AngularFireDatabaseModule } from 'angularfire2/database'; import { AngularFireModule } from 'angularfire2'; //Pages Component import { MyApp } from './app.component'; import { TabsPage } from '../pages/tabs/tabs'; import { HomePage } from '../pages/home/home'; import { ListPage } from '../pages/list/list'; import { LoginPage } from '../pages/login/login'; import { RegisterPage } from '../pages/register/register'; import { ProfilePage } from '../pages/profile/profile'; //Firebase auth key import { FIREBASE_CONFIG } from './config/app.firebase.config'; //Service import { AuthProvider } from '../providers/auth/auth'; @NgModule({ declarations: [ MyApp, TabsPage, HomePage, ListPage, LoginPage, RegisterPage, ProfilePage ], imports: [ BrowserModule, HttpModule, AngularFireDatabaseModule, AngularFireAuthModule, AngularFireModule.initializeApp(FIREBASE_CONFIG), IonicModule.forRoot(MyApp) ], bootstrap: [IonicApp], entryComponents: [ MyApp, TabsPage, HomePage, ListPage, LoginPage, RegisterPage, ProfilePage ], providers: [ StatusBar, SplashScreen, { provide: ErrorHandler, useClass: IonicErrorHandler }, Camera, AuthProvider ] }) export class AppModule { } | cs |
Profile 페이지에 Camera 기능 구현
Camera 기능이 구현될 Profile 페이지를 생성하고 로직을 구현합니다. 갤러리를 통해 다수의 사진의 등록하여 Multiple로 이미지를 업로드 하도록 하는 로직을 구현하도록 하겠습니다.
$ionic g page profile
src/app/pages/profile/profile.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <ion-header> <ion-navbar> <button ion-button menuToggle> <ion-icon name="menu"></ion-icon> </button> <ion-title>Profile</ion-title> </ion-navbar> </ion-header> <ion-content padding class="card-background-page"> <button ion-button full (click)='takePhoto()'><ion-icon name="camera"></ion-icon> Take Photo</button> <button ion-button full (click)='choosePhoto()'><ion-icon name="ios-images"></ion-icon> Select Photo</button> <ion-grid> <ion-row> <ion-col col-6 *ngFor="let photo of photos; let id = index"> <ion-card class="releative"> <ion-icon ios="ios-add-circle" md="md-add-circle" class="deleteIcon" (click)="deletePhoto(id)"></ion-icon> <img [src]="domSanitizer.bypassSecurityTrustUrl(photo)" *ngIf="photo" /> </ion-card> </ion-col> </ion-row> </ion-grid> <button ion-button full (click)='uploadPhoto()'><ion-icon name="ios-cloud-upload-outline"></ion-icon> Upload Photo</button> <ion-grid> <ion-row>{{processString}}</ion-row> </ion-grid> </ion-content> | cs |
src/app/pages/profile/profile.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | import { Component } from '@angular/core'; import { AlertController, ToastController } from 'ionic-angular'; import { DomSanitizer } from '@angular/platform-browser'; import { Camera, CameraOptions } from '@ionic-native/camera'; import firebase from 'firebase'; @Component({ selector: 'page-profile', templateUrl: 'profile.html', }) export class ProfilePage { public photos: any; public base64Image: string; public processString: string; private urlPrefix: string = 'file://'; private base64Prefix: string = 'data:image/jpeg;base64,'; private sendImage: string; private imageblob: any; constructor(public domSanitizer: DomSanitizer, private camera: Camera, private alertCtrl: AlertController, private toastCtrl: ToastController) { } ngOnInit() { this.photos = []; } deletePhoto(index) { let confirm = this.alertCtrl.create({ title: 'Sure you want to delete this photo? There is NO undo!', message: '', buttons: [ { text: 'No', handler: () => { console.log('Disagree clicked'); } }, { text: 'Yes', handler: () => { console.log('Agree clicked'); this.photos.splice(index, 1); //return true; } } ] }); confirm.present(); } choosePhoto() { const options: CameraOptions = { quality: 50, sourceType: this.camera.PictureSourceType.PHOTOLIBRARY, allowEdit: true, destinationType: this.camera.DestinationType.DATA_URL, encodingType: this.camera.EncodingType.JPEG, mediaType: this.camera.MediaType.PICTURE, saveToPhotoAlbum: false }; this.camera.getPicture(options).then((imageData) => { // const safeUrl: any = this.domSanitizer.bypassSecurityTrustUrl(this.base64Prefix + imageData); const safeUrl: any = this.base64Prefix + imageData; this.sendImage = safeUrl; this.photos.push(safeUrl); this.photos.reverse(); }, (err) => { console.log(err); }); } takePhoto() { const options: CameraOptions = { quality: 50, // destinationType: this.camera.DestinationType.FILE_URI, destinationType: this.camera.DestinationType.DATA_URL, encodingType: this.camera.EncodingType.JPEG, mediaType: this.camera.MediaType.PICTURE }; this.camera.getPicture(options).then( (imageData) => { // this.base64Image = this.urlPrefix + imageData; const safeUrl: any = this.base64Prefix + imageData; this.photos.push(safeUrl); this.photos.reverse(); }, (err) => { console.log(err); }); } uploadPhoto() { console.log('uploadPhoto click!'); let storageRef = firebase.storage().ref(); // let storageRef = firebase.storage().refFromURL('gs://moblile-base.appspot.com'); this.processString = ''; this.processString += 'get storage!'; // this.showToast('get storage!'); // Create a timestamp as filename const filename = Math.floor(Date.now() / 1000); console.log('storageRef =>', storageRef.name); // Create a reference to 'images/todays-date.jpg' const imagesRef = storageRef.child('images'); console.log('imagesRef =>', imagesRef); const imageRef = storageRef.child(`images/${filename}.jpg`); // this.showToast('Create a reference!'); this.processString += 'Create a reference!'; console.log('Create a reference!'); const metadata = { contentType: 'image/jpeg', }; // imageRef.put(this.b64toBlob(this.imageblob, 'image/jpeg'), metadata) imageRef.putString(this.sendImage, firebase.storage.StringFormat.DATA_URL).then( (snapshot: any) => { // this.showToast('upload success!'); this.processString += 'upload success!'; }, (err) => { console.log(err); this.processString += 'upload fail => ' + JSON.stringify(err); // this.showToast('upload fail => '+ JSON.stringify(err)); }); } private b64toBlob(b64Data, contentType, sliceSize?) { contentType = contentType || ''; sliceSize = sliceSize || 512; let byteCharacters = atob(b64Data); let byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { let slice = byteCharacters.slice(offset, offset + sliceSize); let byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } let byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } let blob = new Blob(byteArrays, { type: contentType }); return blob; } private showToast(text: string) { let toast = this.toastCtrl.create({ message: text, duration: 5000, position: 'top' }); toast.onDidDismiss(() => { console.log('Dismissed toast'); }); toast.present(); } } | cs |
app.comonent.ts 에서 로그인 했을 경우에 Side menu에서 로그인 페이지 대신 Profile 패이지가 생성되도록 코드를 추가합니다.
src/app/app.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | import { Component, ViewChild } from '@angular/core'; import { Nav, Platform } from 'ionic-angular'; import { StatusBar } from '@ionic-native/status-bar'; import { SplashScreen } from '@ionic-native/splash-screen'; import { AngularFireAuth } from 'angularfire2/auth'; import { TabsPage } from '../pages/tabs/tabs'; import { LoginPage } from '../pages/login/login'; import { ProfilePage } from '../pages/profile/profile'; @Component({ templateUrl: 'app.html' }) export class MyApp { @ViewChild(Nav) nav: Nav; rootPage: any = TabsPage; pages: Array<{ title: string, component: any }>; constructor(public platform: Platform, private afAuth: AngularFireAuth, public statusBar: StatusBar, public splashScreen: SplashScreen) { this.initializeApp(); // used for an example of ngFor and navigation this.pages = [ { title: 'login', component: LoginPage } ]; } initializeApp() { this.afAuth.authState.subscribe(auth => { if (auth) { this.rootPage = TabsPage; this.pages = [ { title: 'Profile', component: ProfilePage } ]; } else { this.rootPage = LoginPage; } }); this.platform.ready().then(() => { // Okay, so the platform is ready and our plugins are available. // Here you can do any higher level native things you might need. this.statusBar.styleDefault(); this.splashScreen.hide(); }); } openPage(page) { // Reset the content nav to have just this page // we wouldn't want the back button to show in this scenario this.nav.setRoot(page.component); } } | cs |
Ionic View를 이용한 테스트
Ionic View를 사용하면 자신의 Native 환경에서 어플리케이션에 대한 베타 테스트를 진행 할 수 있습니다.
Ionic view download, docs url: https://docs.ionic.io/tools/view/
Upload된 프로젝트를 Sync하여 어플리케이션을 실행시킨다.
Loading 화면
Profile 페이지 확인
Camera기능 확인
갤러리를 통해 다수의 사진을 등록한후 Upload하게 되면 FIrebase Console의 Storage탭에서 업로드된 사진에 대한 이름, 유형, 이미지, 생성 시간, 용량 등의 다양한 정보를 확인 할 수 있습니다.
'ionic 3' 카테고리의 다른 글
Ionic & Firebase 사용자 리스트 출력 (0) | 2017.08.06 |
---|---|
Ionic & Firebase 로그인 기능 구현 (2) | 2017.08.06 |
VS code에 Ionic 디버깅 환경 구성 (2) | 2017.08.06 |
Ionic 3 & Firebase 연동 (0) | 2017.08.03 |
Ionic 3 프로젝트 생성 및 실행 (0) | 2017.08.01 |