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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:20:50