Firestore文件上传记录重复创建问题排查求助
问题:Firestore上传文件时生成重复记录
上传文件时,Firestore数据库中会出现重复甚至三条记录,部分重复记录的下载URL后缀token相同,部分不同。相关代码如下:
const useStorage = (file) => { const [progress, setProgress] = useState(0) const [error, setError] = useState(null) const [url, setUrl] = useState(null) useEffect(() => { const storageRef = fbStorageRef( fbStorage, "photo-gallery-reactjs/"+file.name ); const uploadTask = fbUploadBytesResumable(storageRef, file); uploadTask.on('state_changed', (snapshot) => { const progress = ( snapshot.bytesTransferred / snapshot.totalBytes ) * 100; setProgress(progress) }, (error) => { setError(error) }, () => { fbGetDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { const createdAt = timestamp(); const fileName = uploadTask.snapshot.ref.name; fbAddDoc(fbCollection(fbFirestore, "images"), { downloadURL, createdAt, fileName }) setUrl(downloadURL) }) } ) }, [file]) return { progress, url, error } } export default useStorage;
原因分析
- useEffect重复触发:当
file对象引用变化(比如组件重渲染时传入新的file实例,即使文件内容一致),useEffect会重新执行,创建新的上传任务,多次触发完成回调添加记录。另外React开发环境的严格模式会强制双重执行useEffect,也会导致重复上传。 - 同名文件覆盖但记录重复:上传同名文件时,Firebase Storage会覆盖旧文件并生成新的下载URL(部分token不同),但每次上传都会执行
addDoc新增记录。 - 缺少上传任务清理:组件卸载或重渲染时,未取消之前的上传任务,旧任务完成后仍会触发记录添加。
解决方案
- 添加上传任务清理逻辑:在useEffect中返回清理函数,取消未完成的上传任务,避免旧任务继续执行:
useEffect(() => { const storageRef = fbStorageRef( fbStorage, "photo-gallery-reactjs/"+file.name ); const uploadTask = fbUploadBytesResumable(storageRef, file); const unsubscribe = uploadTask.on('state_changed', (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; setProgress(progress) }, (error) => { setError(error) }, () => { fbGetDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { const createdAt = timestamp(); const fileName = uploadTask.snapshot.ref.name; fbAddDoc(fbCollection(fbFirestore, "images"), { downloadURL, createdAt, fileName }) setUrl(downloadURL) }) } ); // 清理函数:取消上传任务并移除监听 return () => { uploadTask.cancel(); unsubscribe(); }; }, [file])
- 稳定useEffect的依赖引用:用文件的唯一标识(名称+大小+最后修改时间)作为依赖,避免因file实例变化触发不必要的重复执行:
const fileKey = `${file.name}-${file.size}-${file.lastModified}`; useEffect(() => { // 上传逻辑不变 }, [fileKey])
- 使用唯一文档ID避免重复记录:放弃
addDoc自动生成ID,改用文件的唯一标识作为文档ID,重复上传时会覆盖旧记录而非新增:
// 生成唯一文档ID const docId = `${file.name}-${file.lastModified}`; // 用setDoc替代addDoc fbSetDoc(fbDoc(fbFirestore, "images", docId), { downloadURL, createdAt, fileName })
内容的提问来源于stack exchange,提问作者Khan Khan
相关产品推荐
相关产品推荐

