1
votes

I am attempting to implement an image uploader component in a Vue.js Firebase application. I set up the uploader to first put the image in Firebase storage using .put(). The downloadURL is then retrieved using .getDownloadURL(), and then saved to the database in order to be rendered throughout the app: this.imageUrl = downloadURL. After uploading several images, I am noticing that downloadURL for some of the images stored in the database appears as a base64 string, while for other images downloadURL appears as a regular URL starting with https://firebasestorage.googleapis.com/.... I want the downloadURL to appear consistently in the database as one or the other. Which format is better for storing in database? How can I set up the code below to consistently interpret in one or the other? My code is below. Thanks!

        var storageRef = firebase.storage().ref()
        var mountainsRef = storageRef.child(`images/${this.imageName}`)
        mountainsRef.put(this.imageFile).then(snapshot => {
          snapshot.ref.getDownloadURL().then(downloadURL => {
            this.imageUrl = downloadURL
          })
        })

Full Component

<template>
  <div class="sign-up">
    <v-container fluid fill-height>
     <v-layout align-center justify-center>
       <v-flex xs12 sm8 md4>
         <v-card class="elevation-8">
           <v-toolbar dark color="primary">
             <v-toolbar-title>Let's create a new account!</v-toolbar-title>
           </v-toolbar>
           <v-card-text>
             <v-form>
               <v-text-field prepend-icon="person" v-model="email" name="email" label="Email" type="text"></v-text-field>
               <v-text-field prepend-icon="lock" v-model="password" name="password" label="Password" id="password" type="password"></v-text-field>
               <v-text-field prepend-icon="lock" v-model="name" name="name" label="Name" id="name" type="text"></v-text-field>

               <v-layout align-center justify-center>
                 <v-flex xs12 sm8 md4>
                   <img :src="imageUrl" height="150" v-if="imageUrl" />
                   <v-text-field label="Select Image" @click="pickFile" v-model="imageName"></v-text-field>
                   <input type="file" style="display: none" ref="image" accept="image/*" @change="onFilePicked"/>
                 </v-flex>
               </v-layout>

             </v-form>
           </v-card-text>
           <v-card-actions>
             <v-container>
               <v-btn @click="signUp" color="info">Sign Up</v-btn>
             </v-container>
           </v-card-actions>
         </v-card>
         <v-card-text>Return to <router-link to="/login"><strong>login</strong></router-link>.</v-card-text>
       </v-flex>
     </v-layout>
   </v-container>
  </div>
</template>
<script>
import slugify from 'slugify'
import firebase from 'firebase'
import db from '@/firebase/init'
export default {
  name: 'signUp',
  data () {
    return {
      email: '',
      password: '',
      name: '',
      slug: null,
      imageName: null,
      imageUrl: '',
      downloadUrl: '',
      imageFile: null
    }
  },
  methods: {
    signUp () {
      if (this.name && this.email && this.password) {
        this.slug = slugify(this.name, {
          replacement: '-',
          remove: /[$*_+~,()'"!\-:@]/g,
          lower: true
        })
        // UPLOAD
        var storageRef = firebase.storage().ref()
        var mountainsRef = storageRef.child(`images/${this.imageName}`)
        mountainsRef.put(this.imageFile).then(snapshot => {
          snapshot.ref.getDownloadURL().then(downloadURL => {
            this.imageUrl = downloadURL
          })
        })
        // end UPLOAD
        let ref = db.collection('users').doc(this.slug)
        ref.get().then(doc => {
          if (doc.exists) {
            this.feedback = 'This alias already exists'
          } else {
            firebase.auth().createUserWithEmailAndPassword(this.email, this.password)
              .then(cred => {
                ref.set({
                  name: this.name,
                  email: this.email,
                  user_id: cred.user.uid,
                  imageUrl: this.imageUrl,
                  downloadUrl: this.downloadUrl
                })
              }).then(() => {
                this.$router.push({ name: 'Dashboard' })
              })
              .catch(err => {
                this.feedback = err.message
              })
            this.feedback = 'This alias is free to use'
          }
        })
      } else {
        this.feedback = 'You must enter all fields'
      }
    },
    pickFile () {
      this.$refs.image.click()
    },
    onFilePicked (e) {
      const files = e.target.files
      if (files[0] !== undefined) {
        this.imageName = files[0].name
        if (this.imageName.lastIndexOf('.') <= 0) {
          return
        }
        const fr = new FileReader()
        fr.readAsDataURL(files[0])
        fr.addEventListener('load', () => {
          this.imageUrl = fr.result
          this.imageFile = files[0]
        })
      } else {
        this.imageName = ''
        this.imageFile = ''
        this.imageUrl = ''
      }
    }
  }
}
</script>
1
The download url should always appear as a regular https url. How are you saving it. What are some examples of URLs that appear differently?Doug Stevenson
Thanks for responding. imageUrl for one image in the database reads as "data:image/jpeg;base64,/<<string of numbers>>", while another reads as "firebasestorage.googleapis.com/v0/b..."JS_is_awesome18
See the updated component above. The code snippet that I posted earlier is part of a larger registration form component. uploading and storing a profile image executed using .set() method. The image renders just fine after submitting the form, but I am wondering why the some images save in the database as base64 while others save with a regular https url.JS_is_awesome18

1 Answers

2
votes

The reason why "downloadURL for some of the images stored in the database appears as a base64 string, while for other images downloadURL appears as a regular URL" is because you set the imageUrl to your user before your Upload part is ready.. so you end up saving this.imageUrl, which is originally set to the FileReader .result.

Instead you should wait until you get the proper downloadURL from the firebase.storage and only then set your user imageUrl.

var storageRef = firebase.storage().ref()
var mountainsRef = storageRef.child(`images/${this.imageName}`)
    mountainsRef.put(this.imageFile).then(snapshot => {
    snapshot.ref.getDownloadURL().then(downloadURL => {
       this.imageUrl = downloadURL

       let ref = db.collection('users').doc(this.slug)
        ref.get().then(doc => {
          if (doc.exists) {
            this.feedback = 'This alias already exists'
          } else {
            firebase.auth().createUserWithEmailAndPassword(this.email, this.password)
              .then(cred => {
                ref.set({
                  name: this.name,
                  email: this.email,
                  user_id: cred.user.uid,
                  imageUrl: this.imageUrl,
                  downloadUrl: this.downloadUrl
                })
              }).then(() => {
                this.$router.push({ name: 'Dashboard' })
              })
              .catch(err => {
                this.feedback = err.message
              })
            this.feedback = 'This alias is free to use'
          }
        })
    })
})