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
额外优化说明
- 避免文件名冲突:添加
avatars/路径前缀和时间戳,防止同名用户覆盖已有头像 - 错误日志打印:在catch块中打印具体错误信息,方便快速定位问题
- 表单值获取优化:通过
name属性获取表单值,避免表单结构变化导致的错误 - 文件选择校验:提前检查用户是否选择文件,避免无意义的上传请求
内容的提问来源于stack exchange,提问作者Deepak Pal
相关产品推荐
相关产品推荐

