In questo articolo vediamo come salvare le foto su Firebase Storage. Anche se questo articolo è parte di una guida ben più ampia che ti fa imparare React Native puoi tranquillamente seguire quanto scritto ed applicare facilmente le brevi procedure necessarie per imparare come salvare le foto su Firebase Storage.
Per questa app stiamo utilizzando il pacchetto firebase disponibile tramite NPM e già descritto in questo articolo.
Come salvare le foto su Firebase Storage
Per scoprire come salvare le foto su Firebase Storage è necessario che prima ti ricordi di alcune cose già descritte nell’articolo relativo a Firebase ed al suo setup iniziale che puoi trovare a questo indirizzo. Firebase Storage è uno spazio nel Cloud costruito per far ospitare file di ogni tipo e specialmente immagini e video.
Il pacchetto firebase installato in precedenza tramite NPM mette a disposizione infatti tutte le API necessarie per comunicare sia con il realtime database, come abbiamo visto quì, che con Firebase Storage come vedremo tra poco.
Segui i passi necessari in ordine e vedrai che anche tu potrai capire come salvare le foto su Firebase Storage.
Installare le dipendenze
Per prima cosa abbiamo bisogno della dipendenza verso react-native-fetch-blob che ci fornisce alcune funzionalità di accesso, download, upload e conversione dei file. Per installarlo puoi digitare da terminale (sempre posizionandoti nella cartella root del tuo progetto):
npm install --save react-native-fetch-blob
Dunque è necessario effettuare il link delle librerie:
react-native link
Modifica dei file
Per capire come salvare le foto su Firebase Storage è necessario che apri il file firebase.js modificandolo in questo modo:
import * as firebase from 'firebase'; import RNFetchBlob from 'react-native-fetch-blob'; const firebaseConfig = { apiKey: "tuoi_dati", authDomain: "tuoi_dati", databaseURL: "tuoi_dati", projectId: "tuoi_dati", storageBucket: "tuoi_dati", messagingSenderId: "tuoi_dati" }; const firebaseApp = firebase.initializeApp(firebaseConfig); export default class Firebase{ constructor(){ firebaseApp.auth().onAuthStateChanged(function(user) { if (user) { //Fai qualcosa.. } else { this.auth(); } }.bind(this)); } auth(){ firebase.auth().signInWithEmailAndPassword("tuoi_dati", "tuoi_dati").catch(function(error) { console.error("Errore durante autenticazione...", error.code, error.message); }); } print(refValue){ console.log("RefValue print: ", refValue); this.itemsRef = firebaseApp.database().ref(refValue); console.log("Ecco la reference: ", this.itemsRef); this.itemsRef.on('value', function(elem){ console.log("elem: ", elem); elem.forEach(function(e){ console.log("e.val() ", e.val()); console.log("e.val().name ", e.val().name); console.log("e.key ", e.key); }); }); } save(refValue, newDataItem){ console.log("RefValue save: ", refValue); firebaseApp.database().ref(refValue + newDataItem.id).set(newDataItem).then(function(result){ console.log("Salvato con successo..!"); }.bind(this), function(error){ console.error("Errore durante il salvataggio..", error); }.bind(this)); } saveImage(settings, path){ const Blob = RNFetchBlob.polyfill.Blob const fs = RNFetchBlob.fs window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest window.Blob = Blob let mime = "image/jpg"; const imageRef = firebase.storage().ref('images').child(settings.id + ".jpg"); fs.readFile(path, 'base64').then(function(data){ return Blob.build(data, { type: `${mime};BASE64` }); }).then(function(blob){ var uploadBlob = blob return imageRef.put(blob, { contentType: mime }) }).then(function(){ uploadBlob.close() return imageRef.getDownloadURL() }).then(function(url){ console.log(url); }).catch(function(error){ console.log("Errore durante il salvataggio su Firebase Storage dell'immagine: ", error); }); } }
E’ rimasto quasi tutto invariato tranne alcune nuove righe aggiunte:
- Importiamo react-native-fetch-blob
- Abbiamo creato un nuovo metodo saveImage che riceve in ingresso dei settings ed un percorso
All’interno del metodo saveImage dichiariamo il Blob ed Fs che ci servirà per accedere al file in memoria. Facciamo lo stesso su “window” così da poter provare il tutto in debug. Dunque definiamo un “mime” e recuperiamo il riferimento alla cartella delle immagini su Firebase Storage. Se accedi a Firebase, entri nel tuo progetto e selezioni “Storage” dal menù laterale sinistro, inizialmente vedrai tutto vuoto. Ciò vuol dire che alla prima chiamata di questo metodo verrà creata la cartella “images” contenente l’immagine. La nostra immagine avrà come nome l’id più l’estensione.
Dunque per prima cosa leggiamo il file in formato base64 e una volta conclusa l’operazione di lettura creiamo un blob. Una volta creato il blob lo teniamo da parte(nella var uploadBlob) e chiamiamo il metodo put di imageRef inviando a Firebase sia il blob che dei metadati (un oggetto con proprietà contentType). Una volta fatto ciò chiudiamo il blob (ecco perché lo teniamo salvato in una variabile a parte) e ritorniamo l’url per il download dell’immagine che poi andiamo a scrivere nei log.
Ma chi è Settings?
Settings è un record della tabella Settings che abbiamo creato in questo articolo.
Prima di capire come salvare le foto con Firebase Storage è necessario che ti spiego cos’è Settings. Questa tabella contiene per scopo dimostrativo i campi id e photoPath. Quello che ci interessa a noi è quindi andare a scrivere in questa tabella il percorso della foto scattata. L’id generato ci farà da riferimento anche nella cartella images di Firebase Storage.
Dunque se non sei interessato puoi sostituire “settings.id” con qualcos’altro e puoi anche decidere di non far ricevere come parametro d’ingresso i settings al metodo saveImage.
Per chi sta seguendo punto per punto la guida è necessario invece creare la cartella “settings” dentro a “common/database”. All’interno della cartella “settings” è necessario creare il file settingsModel.js che conterrà:
import DatabaseHelper from '../databaseHelper'; class SettingsModel { constructor(photoPath) { this.id = DatabaseHelper.guid(); this.photoPath = photoPath; } } module.exports = SettingsModel;
Non è altro che una classe costituita da id e photoPath. L’id viene generato da un DatabaseHelper descritto negli articoli precedenti.
Dunque è necessario creare il file settingsService.js:
import Database from './../database'; import SettingsModel from './settingsModel'; let repository = Database.getRepository(); let SettingsService = { get: function() { return repository.objects('Settings'); }, save: function(setting) { repository.write(() => { repository.create('Settings', setting); }) }, update: function(callback) { if (!callback) return; repository.write(() => { callback(); }); } }; module.exports = SettingsService;
Banalmente implementa i metodi get, save ed update.
L’ultimo step
Ora non ci resta che collegare il tutto e fare le chiamate necessarie dal file photo.js per imparare come salvare le foto con Firebase Storage. Questo file contiene la fotocamera ed inoltre mostra istantaneamente la foto appena scattata.
Adesso lo modificheremo per mostrare la foto salvata (quindi una volta scattata la prima foto chiudendo ed aprendo l’app vedremo sempre l’ultima foto scattata). Inoltre chiameremo il metodo del file firebase.js visto prima per salvare la foto su Firebase Storage.
Apri il file photo.js ed importa Firebase, SettingsModel e SettingsService. Dunque bisogna modificare il metodo takePicture che chiamerà due metodi (writeOnRealm e writeOnFirebasePath). Vediamo insieme il codice completo di photo.js:
import React, { Component } from 'react'; import { StyleSheet, Text, View, Image, Platform } from 'react-native'; import Camera from 'react-native-camera'; import Firebase from "../../common/firebase/firebase"; import SettingsService from '../../common/database/settings/settingsService'; import SettingsModel from '../../common/database/settings/settingsModel'; export default class Photo extends Component<{}> { constructor(props){ super(props); this.camera = undefined; this.firebase = new Firebase(); let settings = SettingsService.get(); this.state = { settings: settings.length > 0 ? settings[0] : undefined, } } onBarCodeRead(e) { console.log( "Barcode Found! ", "Type: " + e.type + "\nData: " + e.data ); } takePicture() { const options = {}; //options.location = ... if(this.camera){ this.camera.capture({metadata: options}).then(function(data){ console.log(data); this.writeOnRealm(data.path); var firebasePath = Platform.OS === 'ios' ? data.path : data.path.substring(7); this.writeOnFirebasePath(firebasePath); }.bind(this)).catch(function(err){ console.error(err); }.bind(this)); } } writeOnRealm(path){ var settings = this.state.settings; if(settings !== undefined){ SettingsService.update(() => { settings.photoPath = path; }); }else{ var settingsModel = new SettingsModel(path); SettingsService.save(settingsModel); } this.setState({settings: SettingsService.get()[0]}); } writeOnFirebasePath(path){ this.firebase.saveImage(this.state.settings, path); } render() { return ( <View style={styles.container}> <Camera ref={(cam) => { this.camera = cam; }} onBarCodeRead={this.onBarCodeRead.bind(this)} style={styles.preview} aspect={Camera.constants.Aspect.fill} captureTarget = {Camera.constants.CaptureTarget.disk}> <Text style={styles.capture} onPress={this.takePicture.bind(this)}>[CAPTURE]</Text> </Camera> <Image source={{ uri: this.state.settings ? this.state.settings.photoPath : undefined }} style={styles.preview}/> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F5FCFF', }, preview: { flex: 1, justifyContent: 'flex-end', alignItems: 'center' }, capture: { flex: 0, backgroundColor: '#fff', borderRadius: 5, color: '#000', padding: 10, margin: 40 } });
Dopo aver importato le nuove dipendenze come descritto poco fa abbiamo modificato il metodo takePicture. Come puoi vedere viene chiamato per primo writeOnRealm che in ordine:
- Controlla se avevamo già i settings
- In caso di risposta positiva esegue un update che consiste nel cambio di path
- Altrimenti crea un nuovo SettingsModel e lo salva
- Dunque nello state imposta in settings quanto abbiamo salvato
Dunque chiamiamo il metodo writeOnFirebasePath che:
- Prende come parametro il path dell’immagine (in questo caso con subString(7) per Android, ovvero eliminando i primi 7 caratteri che sono “file://”)
- Chiama il metodo saveImage scritto prima in firebase.js inviando i settings ed il percorso dell’immagine
Nota Bene: Ho utilizzato Platform importato da react-native per capire se ci troviamo su iOS o su Android.
Nota Bene 2: Quando si utilizza il simulatore iOS è possibile che venga cambiato il percorso ad ogni riavvio quindi sarebbe meglio determinare ogni volta il percorso di base da zero e scrivere sul database solo la parte statica.
Qualche prova
Facciamo qualche prova insieme per vedere cosa accade.
Android
Fai partire la tua app con “react-native run-android” e prova a scattare una foto.
Vedrai che da adesso in poi ci sarà sempre una foto caricata sotto alla fotocamera ed inoltre entrando in Firebase Storage vedrai la cartella “images” con dentro la tua immagine:
Ogni volta che salverai la foto andrai a sovrascrivere questa su Firebase poiché la foto avrà sempre lo stesso nome. Lo stesso vale per il path salvato sul database nella tabella Settings visto che chiameremo il metodo “update” di SettingsService.
Abbiamo già visto come far funzionare la fotocamera in questo articolo. Ovviamente in un’app con degli utenti reali è necessario inserire delle barre di caricamento, una gestione degli errori più strutturata e specialmente stare attenti alle dimensioni delle immagini caricate. Questo però meriterebbe una guida ben più ampia che al momento ti distrarrebbe e basta.
iOS
Fai partire il simulatore iOS con “react-native run-ios” e vai a scattare la tua prima foto.
Entrando su Firebase Storage vedrai ora anche la nuova foto scattata da questo dispositivo:
Conclusioni
In questo articolo hai imparato come salvare le foto su Firebase Storage ed hai potuto toccare con mano il funzionamento sia su iOS che su Android. Abbiamo visto davvero molto in questa guida per imparare React Native e probabilmente con il tempo ci sarà ancora qualcosa di nuovo da aggiungere.
Su Udemy puoi trovare tanti corsi interessanti nei riguardi di React Native:
- The Complete React Native and Redux Course
- React Native: Advanced Concepts
- Create your first React Native App
Non ti perdere quindi la guida salvandoti il link alla guida per imparare React Native.
Se sei interessato continua a seguirmi, magari iscriviti alla newsletter così da non dimenticarti di questa guida. Nel caso in cui ancora non ti senti pronto con React puoi sempre seguire questa guida e poi potrai imparare React Native.
Se vuoi rimanere aggiornato sul continuo di questa guida ti consiglio di iscriverti alla newsletter. Mando da 1 a 4 mail al mese e normalmente invio risorse gratuite e riservate solo agli iscritti. Invio anche la lista degli articoli di maggiore impatto, come questo. Se non troverai gli articoli potrai recuperarli dalla mail in questo modo ?
Per dubbi o domande non esitare a scrivermi nei commenti ?
Se ti è piaciuto l’articolo seguimi su Facebook e Twitter oppure rimani sempre aggiornato con la newsletter (da 1 a 4 mail al mese!).