You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Firebase聊天应用注册时遇Firebase Storage对象不存在错误

Firebase Storage: Object 'xxx' does not exist (storage/object-not-found) 问题排查与解决

问题描述

我正在用React和Firebase开发聊天应用,在接入Firebase Storage时遇到以下错误:

Firebase Storage: Object 'mandy' does not exist. (storage/object-not-found)
FirebaseError: Firebase Storage: Object 'mandy' does not exist. (storage/object-not-found)

已尝试将updateProfile和setDoc放入try-catch块,也修改了Firebase Storage规则为允许读写,但问题依然存在。相关源码如下:

import React from 'react'
import { useState } from 'react';
import Add from '../images/addAvatar.png'
import { createUserWithEmailAndPassword, updateProfile } from "firebase/auth";
import { auth, db, storage } from "../Firebase";
import {  ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { doc, setDoc } from "firebase/firestore";


const Register = () => {
  const [err, setErr] = useState(false)

  const handleSubmit = async (e) =>{
    e.preventDefault()
    const displayName = e.target[0].value;
    const email = e.target[1].value;
    const password = e.target[2].value;
    const file = e.target[3].files[0];

    try{ 
      const res = await createUserWithEmailAndPassword(auth, email, password)

      const storageRef = ref(storage, displayName);
      console.log("Storage Reference:", storageRef.toString()); 

      const uploadTask = uploadBytesResumable(storageRef, file);

      // Register three observers:
      
      uploadTask.on(
        (error) => {
          setErr(true)
        }, 
        () => {

          getDownloadURL(uploadTask.snapshot.ref).then( async (downloadURL) => {
            await updateProfile(res.user,{
              displayName,
              photoURL:downloadURL,
            });
            await setDoc(doc(db, "users", res.user.uid),{
              uid: res.user.uid,
              displayName,
              email,
              photoURL: downloadURL,
            }); 
          });
        }
      );


    }catch(err){
      setErr(true)
    }
      
  }



  return (
    <div className='formContainer' >
      <div className="formWrapper">
        <span className="logo">Real Chat</span>
        <span className="title">Register</span>
        <form onSubmit={handleSubmit} >
          <input type="text" placeholder='Name' />
          <input type="email" placeholder='Email' />
          <input type="password" placeholder='Password' />
          <input style={{display:'none'}}   type="file" id='file' />
          <label htmlFor="file"> <img src={Add} alt="" /> <span>Add an avatar</span> </label>
          <button>Sign up</button>
          {err && <span>Something went wrong</span> }
        </form>
        <p>Already have account ? Login</p>
      </div>
    </div>
  )
}

export default Register

问题根源

你的代码中误用了uploadBytesResumable的on方法回调参数:第二个参数对应的是上传进度更新的next回调,而非上传完成的回调。这会导致上传还未完成时就调用getDownloadURL,此时对象还未完全写入Storage,自然触发"对象不存在"的错误。

另外还有几个潜在问题:

  • 未处理用户未选择文件的场景,会导致file为undefined,触发上传错误
  • 通过e.target[0].value这种索引方式获取表单值不够健壮,表单结构变化时会失效

修复后的代码

import React from 'react'
import { useState } from 'react';
import Add from '../images/addAvatar.png'
import { createUserWithEmailAndPassword, updateProfile } from "firebase/auth";
import { auth, db, storage } from "../Firebase";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { doc, setDoc } from "firebase/firestore";

const Register = () => {
  const [err, setErr] = useState(false)

  const handleSubmit = async (e) =>{
    e.preventDefault()
    // 改用name属性获取表单值,更健壮
    const displayName = e.target.elements.name.value;
    const email = e.target.elements.email.value;
    const password = e.target.elements.password.value;
    const file = e.target.elements.file.files[0];

    // 校验用户是否选择了文件
    if (!file) {
      setErr(true);
      alert('请选择头像文件');
      return;
    }

    try{ 
      const res = await createUserWithEmailAndPassword(auth, email, password)

      // 添加路径前缀和时间戳,避免同名用户覆盖头像
      const storageRef = ref(storage, `avatars/${displayName}_${Date.now()}`); 
      console.log("Storage Reference:", storageRef.toString()); 

      const uploadTask = uploadBytesResumable(storageRef, file);

      // 正确监听上传状态:error, progress, complete
      uploadTask.on(
        'state_changed',
        (snapshot) => {
          // 可选:处理上传进度
          const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
          console.log('上传进度:' + progress + '%');
        },
        (error) => {
          // 处理上传错误
          console.error('上传失败:', error);
          setErr(true);
        },
        async () => {
          // 上传完成后再获取下载链接并更新用户信息
          try {
            const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
            await updateProfile(res.user,{
              displayName,
              photoURL: downloadURL,
            });
            await setDoc(doc(db, "users", res.user.uid),{
              uid: res.user.uid,
              displayName,
              email,
              photoURL: downloadURL,
            });
            console.log('注册成功');
          } catch (updateErr) {
            console.error('更新用户信息失败:', updateErr);
            setErr(true);
          }
        }
      );

    }catch(err){
      console.error('创建用户失败:', err);
      setErr(true);
    }
  }

  return (
    <div className='formContainer' >
      <div className="formWrapper">
        <span className="logo">Real Chat</span>
        <span className="title">Register</span>
        <form onSubmit={handleSubmit} >
          {/* 给input添加name属性 */}
          <input type="text" name="name" placeholder='Name' />
          <input type="email" name="email" placeholder='Email' />
          <input type="password" name="password" placeholder='Password' />
          <input style={{display:'none'}} type="file" id='file' name="file" />
          <label htmlFor="file"> <img src={Add} alt="" /> <span>Add an avatar</span> </label>
          <button>Sign up</button>
          {err && <span>Something went wrong</span> }
        </form>
        <p>Already have account ? Login</p>
      </div>
    </div>
  )
}

export default Register

额外优化说明

  1. 避免文件名冲突:添加avatars/路径前缀和时间戳,防止同名用户覆盖已有头像
  2. 错误日志打印:在catch块中打印具体错误信息,方便快速定位问题
  3. 表单值获取优化:通过name属性获取表单值,避免表单结构变化导致的错误
  4. 文件选择校验:提前检查用户是否选择文件,避免无意义的上传请求

内容的提问来源于stack exchange,提问作者Deepak Pal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 03:08:25