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

ReactJS SimpChat注册表单报错:Firebase Storage对象不存在问题排查

错误原因分析

报错Firebase Storage: Object 'john' does not exist的核心问题有两个:

  1. uploadTask回调参数顺序错误
    你在uploadTask.on里把完成逻辑放在了第二个参数位置,但Firebase Storage的uploadBytesResumable.on方法中,第二个参数是进度快照回调(每次上传进度变化时触发),第三个参数才是上传完成的回调。现在代码里在上传还未完成(甚至未开始)时就调用getDownloadURL,此时Storage中还没有目标对象,自然会报“对象不存在”的错误。

  2. 未处理用户未选择头像文件的情况
    如果用户未点击选择头像,file变量会是undefined,此时调用uploadBytesResumable会导致上传逻辑异常,进一步触发后续getDownloadURL找不到对象的错误。

另外还有两个潜在问题:

  • 直接用displayName作为Storage路径,若有同名用户,会覆盖之前的头像文件。
  • 错误处理不完整,Storage的错误仅设置了err=true,没有具体错误信息,不利于排查问题。
解决方案

以下是修正后的完整代码,附带关键注释:

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

const Register = () => {
  const [err, setErr] = useState(false);
  const [errMsg, setErrMsg] = useState("");

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

    // 基础表单验证:检查必填项和文件是否存在
    if (!displayName || !email || !password) {
      setErrMsg("请填写所有必填字段");
      setErr(true);
      return;
    }
    if (!file) {
      setErrMsg("请选择头像文件");
      setErr(true);
      return;
    }

    try {
      // 创建用户
      const res = await createUserWithEmailAndPassword(auth, email, password)

      // 优化Storage路径:加上前缀和时间戳,避免同名覆盖
      const storageRef = ref(storage, `avatars/${displayName}_${Date.now()}`);
      const uploadTask = uploadBytesResumable(storageRef, file);
      
      // 修正uploadTask的回调顺序:错误 → 进度 → 完成
      uploadTask.on(
        'state_changed',
        // 进度回调(可选,可用于显示上传进度)
        (snapshot) => {
          const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
          console.log(`上传进度:${progress}%`);
        },
        // 错误回调
        (error) => {
          console.error("Storage上传错误:", error);
          setErrMsg(error.message);
          setErr(true);
        },
        // 完成回调:上传成功后才获取下载链接
        async () => {
          try {
            const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
            // 更新用户Profile
            await updateProfile(res.user, {
              displayName,
              photoURL: downloadURL,
            });
            // 写入Firestore
            await setDoc(doc(db, "users", res.user.uid), {
              uid: res.user.uid,
              displayName,
              email,
              photoURL: downloadURL,
            });
            // 注册成功后的逻辑,比如跳转到登录页或主页
            console.log("注册成功");
          } catch (dbErr) {
            console.error("Firestore写入错误:", dbErr);
            setErrMsg(dbErr.message);
            setErr(true);
          }
        }
      );

    } catch (authErr) {
      console.error("Auth创建用户错误:", authErr);
      setErrMsg(authErr.message);
      setErr(true);
    }
  }

  return (
    <div className='formContainer'>
        <div className='formWrapper'>
            <span className="logo">SimpChat</span>
            <span className="logo">Register</span>
            
            <form onSubmit={handleSubmit}>
                <input type="text" placeholder='display 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>{errMsg || 'Something went wrong'}</span>}
            </form>

            <p>You do have an account? Login</p>

        </div>
    </div>
  )
}

export default Register
关键修改点说明
  • 回调顺序修正:把上传完成的逻辑移到uploadTask.on的第三个参数,确保只有上传成功后才去获取文件下载链接。
  • 表单与文件验证:增加必填字段和文件选择的检查,提前拦截无效提交。
  • Storage路径优化:用avatars/${displayName}_${Date.now()}作为路径,避免同名文件覆盖,同时分类存储头像文件。
  • 错误信息细化:添加errMsg状态,显示具体错误信息,方便调试和用户查看。
  • 嵌套错误捕获:在完成回调内部添加try/catch,捕获Firestore写入时的错误。

内容的提问来源于stack exchange,提问作者John Kelvin Atienza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 02:50:42