ReactJS SimpChat注册表单报错:Firebase Storage对象不存在问题排查
错误原因分析
报错Firebase Storage: Object 'john' does not exist的核心问题有两个:
uploadTask回调参数顺序错误
你在uploadTask.on里把完成逻辑放在了第二个参数位置,但Firebase Storage的uploadBytesResumable.on方法中,第二个参数是进度快照回调(每次上传进度变化时触发),第三个参数才是上传完成的回调。现在代码里在上传还未完成(甚至未开始)时就调用getDownloadURL,此时Storage中还没有目标对象,自然会报“对象不存在”的错误。未处理用户未选择头像文件的情况
如果用户未点击选择头像,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
相关产品推荐
相关产品推荐

