Как загрузить файл в хранилище Firebase, а затем сохранить URL-адрес внутри firestore с помощью vue 3 (API композиции)

я новичок в firebase и vue 3, и у меня есть проблемы с загрузкой в ​​​​хранилище, я загружаю файл (изображение) в firebase и хочу сохранить его в firestore со всеми другими данными из входов, я смог сохранить все данные из формы но не могу с URL-адресом файла, я использую эту функцию:

<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.info('Upload is ' + progress + '% done');
    switch (snapshot.state) {
      case 'paused':
        console.info('Upload is paused');
        break;
      case 'running':
        console.info('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.info('File available at', downloadURL);

      resolve({ imgUrl: downloadURL });
    });
  });
}
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
132
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Прежде всего - я сделал простую форму...

<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>

... и подключил его скриптом

<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.info('Upload is ' + progress + '% done')
        },
        (error) => {
          console.info(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>

Вы можете заметить линию import { storage, productsCollectionRef } from '../firebase/config'. Это просто еще один js-файл, в котором я передаю конфигурацию для 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 }

спасибо Богдан за вашу помощь, это работает как шарм, у меня есть еще один вопрос, если возможно, что, если мы хотим загрузить несколько файлов, этот код все равно будет работать так же?

bahy llam 09.04.2023 08:41

@bahyllam нужно добавить несколько изменений. 1. добавьте атрибут multiple к вашему входному полю 2. в handleFileChange fn - сохраните все выбранные файлы (например, в массиве) 3. в uploadFile fn вам нужно перебрать массив и дождаться загрузки всех файлов, а затем вернуть все URL-адреса ( также в массиве, например) и, наконец, вызовите createProduct с этими данными.

Bohdan 09.04.2023 13:23

@bahyllam у меня недостаточно репутации, чтобы добавить комментарий.

в вашем предыдущем коде (10 апреля, 5:05) у вас есть массив в this.files, но ваша функция uploadFile(file) принимает один file в качестве аргумента. Итак, внутри этой функции нужно сделать цикл для всего массива и вызвать весь код для каждого file отдельно (получить name и type для каждого файла, сделать storageRef и uploadTask. НО только один раз сделать операцию записи:

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.info('Upload is ' + progress + '% done')
        },
        (error) => {
          console.info(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)
  })
}

Другие вопросы по теме