skip to Main Content

im new to firebase and vue 3 and i have prob dealing with upload to storage, i am uploading the file (image) on firebase and want to store it to firestore with all other data from inputs, i was able to save all data from form but cant with the file url, im using this fucntion :

<input @change="uploadTaskPromise" class="file-input" type="file" name="file">
import { getStorage, ref as stRef, uploadBytesResumable, getDownloadURL } from "firebase/storage";



const uploadTaskPromise = (e) => {
  const file = e.target.files[0];
  const storage = getStorage();

  const metadata = {
  contentType: 'image/jpeg'
};
  
  const storageRef = stRef(storage, 'products/' + file.name);

  const uploadTask = uploadBytesResumable(storageRef, file, metadata);

  
uploadTask.on('state_changed',
  (snapshot) => {
    
    const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
    console.log('Upload is ' + progress + '% done');
    switch (snapshot.state) {
      case 'paused':
        console.log('Upload is paused');
        break;
      case 'running':
        console.log('Upload is running');
        break;
    }
  }, 
  (error) => {
    
    switch (error.code) {
      case 'storage/unauthorized':
        
        break;
      case 'storage/canceled':
        
        break;

      // ...

      case 'storage/unknown':
        
        break;
    }
  }, 
  () => {
    
    getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {


      console.log('File available at', downloadURL);

      resolve({ imgUrl: downloadURL });
    });
  });
}

2

Answers


  1. First of all – i’ve made a simple form …

    <template>
      <form action="#" @submit.prevent>
        <div>
          <label for="title">Product title</label>
          <input v-model="title" type="text" id="title" placeholder="Title" />
        </div>
        <div>
          <label for="price">Product price</label>
          <input v-model="price" type="number" id="price" min="0" placeholder="0.00" />
        </div>
        <div>
          <label for="file">Product photo</label>
          <input @change="handleFileChange" type="file" id="file" />
        </div>
    
        <button @click="submitForm">Send</button>
      </form>
    </template>
    

    … and connected it with script

    <script>
    // this variables setup-ed in config.js
    import { storage, productsCollectionRef } from '../firebase/config'
    // this function from firebase. need to upload file
    import { ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage'
    // this one for creating document in database (firestore) 
    import { addDoc } from 'firebase/firestore'
    
    export default {
      // child component, imported in main App.vue
      name: 'vue-upload',
      data() {
        return {
          title: null,
          price: 0,
          file: null
        }
      },
      methods: {
        handleFileChange(e) {
          // when file selected - update property in data()
          this.file = e.target.files[0]
        },
    
        createProduct(data) {
          // pass a reference to collection and data
          addDoc(productsCollectionRef, data)
        },
    
        uploadFile(file) {
          // getting name & type properties from File object
          const { name, type } = file
          // create a reference to file in firebase
          const storageRef = ref(storage, 'images/' + name)
          // upload file (this.file) to firebase and append metadata
          const uploadTask = uploadBytesResumable(storageRef, this.file, {
            contentType: type
          })
    
          uploadTask.on(
            'state_changed',
            (snapshot) => {
              const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
              console.log('Upload is ' + progress + '% done')
            },
            (error) => {
              console.log(error)
            },
            () => {
              // successful upload. get url ...
              getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
                // ... make a data to write ...
                const data = {
                  title: this.title,
                  price: this.price,
                  url: downloadURL
                }
                // ... and call a fn that writes a document to a firestore
                this.createProduct(data)
              })
            }
          )
        },
    
        submitForm() {
          // just quick & simple validation
          if (this.title && this.price && this.file) {
            this.uploadFile(this.file)
            return
          }
    
          alert('Form invalid')
        }
      }
    }
    </script>
    

    You may notice a import { storage, productsCollectionRef } from '../firebase/config' line.
    It’s just another one js-file, where i pass configuration for firebase:

    ./firebase/config.js

    import { initializeApp } from 'firebase/app'
    
    import { getStorage } from 'firebase/storage'
    import { getFirestore, collection } from 'firebase/firestore'
    
    const firebaseConfig = {
      apiKey: 'XXXXX',
      authDomain: 'XXXXX',
      projectId: 'XXXXX',
      storageBucket: 'XXXXX',
      messagingSenderId: 'XXXXX',
      appId: 'XXXXX'
    }
    
    // Initialize Firebase
    const app = initializeApp(firebaseConfig)
    const storage = getStorage(app)
    const db = getFirestore(app)
    
    // i've made a 'products' collection in my firestore database [screenshot][1]
    const productsCollectionRef = collection(db, 'products')
    // we need export this setuped variables for use in another file
    export { storage, productsCollectionRef }
    
    Login or Signup to reply.
  2. @bahyllam i don’t have enough reputation to add a comment.

    in your previous code (Apr 10 at 5:05), you have an array in this.files, but your uploadFile(file) function takes a single file as an argument.
    So inside this function you need to make a loop for the whole array and call all the code for each file separately (get a name & type for each file, make storageRef and uploadTask. BUT only once make a write operation:

    handleFileChange(e) {
        if (e.target.files) {
            for (const file of e.target.files) {
                this.files.push(file)
            }
        }
    }
    
    uploadFile() {
      const urls = []
      const promises = []
    
      this.files.forEach((file) => {
        // getting name & type properties from each File object
        const { name, type } = file
        // create a reference to each file in firebase
        const storageRef = ref(storage, 'images/' + name)
        // upload each file to firebase and append metadata
        const uploadTask = uploadBytesResumable(storageRef, file, {
          contentType: type
        })
    
        // create task with promises. this will help us to expect the result correctly
        const promise = new Promise((resolve, reject) => {
          uploadTask.on(
            'state_changed',
            (snapshot) => {
              const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
              console.log('Upload is ' + progress + '% done')
            },
            (error) => {
              console.log(error)
              reject(error)
            },
            async () => {
              // wait for URL from getDownloadURL 
              const url = await getDownloadURL(uploadTask.snapshot.ref)
              urls.push(url)
              resolve(url)
            }
          )
        })
    
        // add task to "waitlist"
        promises.push(promise)
      })
    
      // when all "waitlist" resolve ...
      Promise.all(promises).then(() => {
        // ... make a data to write ...
        const data = {
          title: this.title,
          price: this.price,
          // url: [Array]
          url: urls
        }
        // ... and call a fn that writes a document to a firestore
        this.createProduct(data)
      })
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search