What we’re building https://www.youtube.com/watch?v=Dhx1Wu6Gzu8 We’ll build a cross-platform mobile app for taking photos and uploading to firebase. In Part 1, we’ll take a picture and save it to Firebase Cloud Storage, and then show it in our app. In Part 2, we’ll offload the uploading work, and use the blueimp library to generate a thumbnail locally and show it while uploading. The stack Vue JS — Component framework Cordova — Cross platform mobile framework Quasar — UI framework (and CLI) Firebase Cloud Storage — For storing the photos Web Workers — For offloading the uploading to a separate thread Scaffolding We’ll use to initialize a new project, and run the cordova mode (android or ios) to see the app running on your connected device. Quasar CLI quasar create vue-firebase-image-upload vue-firebase-image-upload quasar dev -m android cd You’ll have to add https: true in the devServer section of quasar.conf.js if running on android > 9 You should get a basic working version that looks like this: This is a good time to make your first commit. For more details on how to install and setup quasar, see this post Taking a picture and getting base64 The way to store images in Firebase Cloud Storage is by saving the base-64 string of the image, using Firebase’s method. Notice that you have to remove the base64 prefix from the string before uploading, or Firebase will reject the string. putString To begin, we'll add a button that takes a picture using Cordova's camera plugin, and print the base64 string. Adding plugins First, add cordova camera plugin and file plugin: src-cordova cordova plugin add cordova-plugin-file cordova plugin add cordova-plugin-camera cd Taking the picture In order to take a picture, we’ll add some code in a new service file : src/services/cordova-camera.js { ( { camera = navigator.camera; options = { : , : camera.DestinationType.FILE_URI, : camera.EncodingType.JPG, : camera.MediaType.PICTURE, : , : }; camera.getPicture( { .resolveLocalFileSystemURL(imageURI, { fileEntry.file( { resolve(fileObject) }, { .error(err); reject(err); } ); }, { } ); }, .error, options ); }) } { ( { reader = FileReader() reader.onloadend = { image = Image() image.onload = { resolve(evt.target.result) } image.src = evt.target.result } reader.readAsDataURL(fileObject) }) } { fileObject = getCameraFileObject(); base64 = getBase64FromFileObject(fileObject); base64; } { getBase64FromCamera } async ( ) function getCameraFileObject return new Promise ( ) => resolve, reject let const quality 50 destinationType encodingType mediaType saveToPhotoAlbum true correctOrientation true => imageURI window ( ) function fileEntry ( ) function fileObject ( ) function err console ( ) function console async ( ) function getBase64FromFileObject fileObject return new Promise ( ) => resolve, reject var new ( ) function evt var new ( ) function e async ( ) function getBase64FromCamera let await let await return export default This service is doing several steps: Takes a picture using Cordova’s camera plugin. This returns an image URI. Gets a file using Cordova’s file plugin, with function. entry resolveLocalFileSystemURL Gets a File using the file method. object Uses FileReader to get the base64 representation of the file. Now let’s add a simple event bus. Add the file with content: src/services/event-bus.js Vue ; EventBus = Vue(); import from 'vue' export const new Now in , we'll add a button in the toolbar for taking a picture, and use our event bus to send the handling to : src/layouts/MyLayout.vue Index.vue ... ... < > template < @ = > q-btn flat dense round click "takePicture" < = /> q-icon name "camera" </ > q-btn </ > template < > script { EventBus } ; { ... methods: { takePicture() { EventBus.$emit( ) } } } import from "../services/event-bus.js" export default 'takePicture' </ > script Finally we’ll catch the takePicture event in our main component: , call the service function and print the base64 result (which we'll later upload to firebase): In file in the <script> section: Index.vue cordova-camera.js Index.vue { EventBus } ; cordovaCamera ; { : , mounted() { EventBus.$off( ); EventBus.$on( , .uploadImageFromCamera); }, : { uploadImageFromCamera() { base64 = cordovaCamera.getBase64FromCamera(); .log( , base64) } } }; import from "../services/event-bus" import from "../services/cordova-camera" export default name "PageIndex" "takePicture" "takePicture" this methods async let await console "base64" Running with , and looking at the chrome dev tools, we can see the base64 output as a long string printed in the console: quasar dev -m android Adding firebase Web SDK or native SDK? When using Firebase in a Cordova app, you can choose between using the Web SDK and a cordova plugin the wraps the Native SDKs. The state of the current Firebase cordova plugins seems a little unstable to me, not fully supporting Firebase Cloud Storage yet. And with the ability to offload work to a web worker, we can use the full-featured official Web SDK, and not take the performance hit. Add the firebase sdk using yarn: yarn add firebase Firebase setup Follow the instructions in the Firebase Cloud Storage . After the setup you should have a firebase project with Storage enabled. We’ll save the settings in a separate file: (this file won't be committed to our repo). setup page firebase-config.js The contents should look something like this: { : { : , : , : , : } } export default firebase apiKey '<your-api-key>' authDomain '<your-auth-domain>' databaseURL '<your-database-url>' storageBucket '<your-storage-bucket-url>' Uploading The Image Now that we have Firebase settings in place, we’ll add another service for handling Firebase app initialization and uploading. Add the file with content: src/services/cloud-storage.js * firebase ; ; firebaseConfig ; { ( { uploadTask = firebase.storage().ref().child(storageId).putString(imageData, ); uploadTask.on( , {}, { reject(error) }, { uploadTask.snapshot.ref .getDownloadURL() .then( { .log( ); .log( , downloadURL); resolve(downloadURL); }); } ); }); } { firebase.initializeApp(firebaseConfig.firebase) } { uploadBase64, initialize } import as from "firebase/app" import 'firebase/storage' import from './firebase-config' async ( ) function uploadBase64 imageData, storageId return new Promise ( ) => resolve, reject let "base64" "state_changed" ( ) function snapshot ( ) function error ( ) function ( ) function downloadURL console "Uploaded a blob or file!" console "got downloadURL: " ( ) function initialize export default The function uploads imageData (the base64 string) using the method, under the Firebase child of . You can have any ID you want, here I've chosen to use the unix datetime number as the child ID. After the uploading is done, we return the image URL using function of the uploading task. uploadBase64 putString storageId getDownloadURL In our main file we'll call the initialize function when the app is mounted: App.vue cloudStorage { : , mounted() { cloudStorage.initialize(); } } import from './services/cloud-storage' export default name 'App' Finally we’ll call the uploading function from . Index.vue This is now the content of : Index.vue < > template < > q-page < = = = > div class "row justify-center q-ma-md" v-for "(pic, idx) in pics" :key "idx" < = > div class "col" < > q-card < = = /> q-img spinner-color "white" :src "pic" </ > q-card </ > div </ > div </ > q-page </ > template < > script { EventBus } ; cordovaCamera ; cloudStorage ; { : , data() { { : [] }; }, mounted() { EventBus.$off( ); EventBus.$on( , .uploadImageFromCamera); }, : { removeBase64Prefix(base64Str) { base64Str.substr(base64Str.indexOf( ) + ); }, uploadImageFromCamera() { base64 = cordovaCamera.getBase64FromCamera(); imageData = .removeBase64Prefix(base64); storageId = ().getTime().toString(); uploadedPic = cloudStorage.uploadBase64(imageData, storageId); .pics.push(uploadedPic); } } }; import from "../services/event-bus" import from "../services/cordova-camera" import from "../services/cloud-storage" export default name "PageIndex" return pics "takePicture" "takePicture" this methods return "," 1 async const await const this const new Date const await this </ > script We’ve added the pics array to our component's data, containing URLs of all the pictures we uploaded. We've added a v-for that will show each picture in a quasar component inside a component. We've also changes to take a picture, get the base64 string (stripping the prefix so Firebase won't freak out), calculated the using the current datetime, and called the uploading function. After it's uploaded, we add the resulting URL to the pics array. q-img q-card uploadImageFromCamera storageId And we can now see the uploaded image: The full code is on . GitHub In the next part of the series we’ll move tasks to a web worker, add a loading spinner, and save the URLs in local storage. Stay tuned!