Что является причиной невозможности загрузки изображения в Firebase Store в этом приложении React 18?

Я создаю приложение для чата с помощью React 18 и Firebase 9.

У меня возникла проблема при разработке функции загрузки изображений для формы «Регистрация».

В src\firebaseConfig.js у меня есть:

import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getStorage } from "firebase/storage";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID
};

// Initialize Firebase
export const app = initializeApp(firebaseConfig);
export const auth = getAuth();
export const storage = getStorage();
export const db = getFirestore();

В компоненте src\pages\Register.jsx у меня есть:

import React, { useState } from "react";
import md5 from "md5";
import FormCard from "../components/FormCard/FormCard";
import { make } from "simple-body-validator";
import { createUserWithEmailAndPassword, updateProfile } from "firebase/auth";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { doc, setDoc } from "firebase/firestore";
import { auth, db, storage } from "../firebaseConfig";

export default function Register() {
  const initialFormData = {
    firstName: "",
    lastName: "",
    email: "",
    avatar: "",
    password: "",
    password_confirmation: ""
  };

  const validationRules = {
    firstName: ["required", "string", "min:3", "max:255"],
    lastName: ["required", "string", "min:3", "max:255"],
    email: ["required", "email"],
    avatar: ["required"],
    password: ["required", "min:6", "confirmed"],
    password_confirmation: ["required"]
  };

  const validator = make(initialFormData, validationRules);
  const [formData, setFormData] = useState(initialFormData);

  // Form validation errors
  const [errors, setErrors] = useState(validator.errors());
  // Firebase errors
  const [error, setError] = useState(false);

  const [pristineFields, setPristineFields] = useState(() =>
    Object.keys(initialFormData)
  );

  const handleChangeEvent = (event) => {
    const { name, value } = event.target;
    setFormData((prevFormData) => {
      const newFormData = { ...prevFormData, [name]: value };
      const newPristineFields = pristineFields.filter((f) => f !== name);

      validator.setData(newFormData).validate();
      const validationErrors = validator.errors();
      newPristineFields.forEach((f) => validationErrors.forget(f));
      setErrors(validationErrors);

      setPristineFields(newPristineFields);

      return newFormData;
    });
  };

  const handleSubmit = async (event) => {
    event.preventDefault();

    if (!validator.setData(formData).validate()) {
      setErrors(validator.errors());
    } else {

      const email = formData.email;
      const password = formData.password; 
      const successContainer =  document.getElementById('successAlert');
      const errContainer =  document.getElementById('errorAlert');

      try {
        const response = await createUserWithEmailAndPassword(auth, email, password);
        // Hide error
        errContainer.classList.add('d-none');

        // Show success
        successContainer.classList.remove('d-none');

        // Create avatar filename
        const date = new Date().getTime();
        let userAvatar = formData.avatar ? md5(`${formData.email}-${date}`) + `.${formData.avatar.split('.')[1]}` : 'default-avatar.png';
        const storageRef = ref(storage, userAvatar);

        // Upload image
        await uploadBytesResumable(storageRef, formData.avatar).then(() => {
          getDownloadURL(storageRef).then(async () => {
            try {
              // Update profile
              await updateProfile(response.user, {
                avatar: userAvatar,
              });

              // Store user
              await setDoc(doc(db, "users", response.user.uid), {
                uid: response.user.uid,
                firstName: formData.firstName,
                lastName: formData.lastName,
                email: formData.email,
                avatar: userAvatar,
              });
            } catch (error) {
              setError(true);
              console.info(error)
            }
          })
        })

      } catch (error) {
        setError(true);
        errContainer.append(error.message);
        errContainer.classList.remove('d-none');
      }
    }

    setPristineFields([]);
  };

  return (
    <FormCard title = "Register">
      <div id = "errorAlert" class = "d-none text-center alert alert-danger alert-dismissible">
        <button type = "button" class = "btn-close" data-bs-dismiss = "alert"></button>
        {error}
      </div>

      <div id = "successAlert" class = "d-none text-center alert alert-success alert-dismissible">
        <button type = "button" class = "btn-close" data-bs-dismiss = "alert"></button>
        You have registered successfully! 
      </div>

      <form onSubmit = {handleSubmit}>
        <div
          className = {`mb-2 form-element ${errors.has("firstName") ? "has-error" : null
            }`}
        >
          <label for = "firstName" className = "form-label">
            First name
          </label>
          <input
            type = "text"
            id = "firstName"
            name = "firstName"
            value = {formData.firstName}
            onChange = {handleChangeEvent}
            className = "form-control form-control-sm"
          />
          {errors.has("firstName") ? (
            <p className = "invalid-feedback">{errors.first("firstName")}</p>
          ) : null}
        </div>

        <div
          className = {`mb-2 form-element ${errors.has("lastName") ? "has-error" : null
            }`}
        >
          <label for = "lastName" className = "form-label">
            Last name
          </label>
          <input
            type = "text"
            id = "lastName"
            name = "lastName"
            value = {formData.lastName}
            onChange = {handleChangeEvent}
            className = "form-control form-control-sm"
          />
          {errors.has("lastName") ? (
            <p className = "invalid-feedback">{errors.first("lastName")}</p>
          ) : null}
        </div>

        <div
          className = {`mb-2 form-element ${errors.has("email") ? "has-error" : null
            }`}
        >
          <label for = "email" className = "form-label">
            Email address
          </label>
          <input
            type = "email"
            id = "email"
            name = "email"
            value = {formData.email}
            onChange = {handleChangeEvent}
            className = "form-control form-control-sm"
          />
          {errors.has("email") ? (
            <p className = "invalid-feedback">{errors.first("email")}</p>
          ) : null}
        </div>

        <div
          className = {`mb-2 form-element ${errors.has("avatar") ? "has-error" : null
            }`}
        >
          <label for = "avatar" className = "form-label">
            Avatar
          </label>
          <input
            type = "file"
            id = "avatar"
            name = "avatar"
            value = {formData.avatar}
            onChange = {handleChangeEvent}
            className = "form-control form-control-sm"
          />
          {errors.has("avatar") ? (
            <p className = "invalid-feedback">{errors.first("avatar")}</p>
          ) : null}
        </div>

        <div
          className = {`mb-2 form-element ${errors.has("password") ? "has-error" : null
            }`}
        >
          <label for = "password" className = "form-label">
            Password
          </label>
          <input
            type = "password"
            id = "password"
            name = "password"
            value = {formData.password}
            onChange = {handleChangeEvent}
            className = "form-control form-control-sm"
          />
          {errors.has("password") ? (
            <p className = "invalid-feedback">{errors.first("password")}</p>
          ) : null}
        </div>

        <div
          className = {`mb-2 form-element ${errors.has("password_confirmation") ? "has-error" : null
            }`}
        >
          <label for = "password_repeat" className = "form-label">
            Confirm Password
          </label>
          <input
            type = "password"
            id = "password_repeat"
            name = "password_confirmation"
            value = {formData.password_confirmation}
            onChange = {handleChangeEvent}
            className = "form-control form-control-sm"
          />
          {errors.has("password_confirmation") ? (
            <p className = "invalid-feedback">
              {errors.first("password_confirmation")}
            </p>
          ) : null}
        </div>

        <div className = "pt-1">
          <button type = "submit" className = "btn btn-sm btn-success fw-bold">
            Submit
          </button>
        </div>
      </form>
    </FormCard>
  );
}

Данные формы успешно отправлены, как показано ниже.

Проблема

Изображение, которое должно быть загружено в хранилище Firebase, имеет тип «application/octet-stream» вместо «image/jpeg» и имеет размер менее 10 байт.

Вопросы

  1. Что я делаю не так?
  2. Какой самый надежный способ решить проблему?
Поведение ключевого слова "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) для оценки ваших знаний,...
0
0
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Как упоминалось в документации,

Если метаданные contentType не указаны и файл не имеет расширения, Cloud Storage по умолчанию использует тип application/octet-stream.

const meta = { contentType: 'image/jpeg' };

await uploadBytesResumable(storageRef, formData.avatar, meta).then(() => {
    getDownloadURL(storageRef).then(async (url) => {
        try {
            // Update profile
            await updateProfile(response.user, {
                avatar: url,
            });

            // Store user
            await setDoc(doc(db, "users", response.user.uid), {
                uid: response.user.uid,
                firstName: formData.firstName,
                lastName: formData.lastName,
                email: formData.email,
                avatar: userAvatar,
            });
        } catch (error) {
            setError(true);
            console.info(error)
        }
    })
})

С другой стороны, похоже, что в Firebase загружается только строка пути к файлу, а не весь файл.

Исправленный:

  • Удалите параметр «value=» из файла, так как он не нужен.
<input
  type = "file"
  id = "avatar"
  name = "avatar"
  onChange = {handleChangeEvent}
  className = "form-control form-control-sm"
/>;
  • Проверьте, есть ли какой-либо файл из события ввода onChange.
const handleChangeEvent = (event) => {
    const { name, value, files } = event.target;
    setFormData((prevFormData) => {
      const newValue = files?.length > 0 ? files[0] : value;
      const newFormData = { ...prevFormData, [name]: newValue };
  • Удалите валидаторы и получите правильное имя файла.
let userAvatar = formData.avatar
          ? md5(`${formData.email}-${date}`) + '.' + formData.avatar.name.split(".")[1]
          : "default-avatar.png";

Отредактировал ответ. Взгляни.

Shri Hari L 26.09.2023 12:48

Пожалуйста, попробуйте ответить и на этот вопрос. Спасибо!

Razvan Zamfir 29.09.2023 10:14

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