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

Next.js13+Firebase v9中Blob图片上传Firebase Storage报错解决

问题分析与解决方案

错误原因

你当前的代码逻辑完全颠倒了:

  • 你试图通过本地Blob URL去Firebase Storage获取文件下载链接,但这个文件从未上传到Storage,自然会触发storage/object-not-found错误。
  • 代码中只调用了getDownloadURL,没有执行任何上传文件到Storage的操作。
  • Redux里仅存储了Blob URL,没有保留实际的File/Blob对象,导致没有可上传的资源。

修正步骤

1. 修正拖拽上传组件:存储实际文件对象

修改onDrop逻辑,将压缩后的compressedFile对象一同存入state和Redux,而非仅存储Blob URL:

const dispatch = useDispatch();
const images = useSelector(selectImages);
const [files, setFiles] = useState(images.length === 0 ? [] : images);

const {getRootProps, getInputProps} = useDropzone({
  onDrop: async (acceptedFiles) => {
    // 先处理数量限制,避免后续无效操作
    const tot = acceptedFiles.length + files.length;
    if (tot > 9) {
      alert("最多选择9张图片");
      return;
    }
    if (acceptedFiles.length > 9) {
      alert("单次最多选择9张图片");
      return;
    }

    // 处理重复文件(用文件名判断,也可改用文件hash提升准确性)
    const existingNames = files.map(f => f.name);
    const newFiles = acceptedFiles.filter(file => !existingNames.includes(file.name));
    if (newFiles.length !== acceptedFiles.length) {
      alert("检测到重复图片,已自动过滤");
    }

    // 批量压缩并生成包含文件对象的数组
    const compressedFiles = await Promise.all(
      newFiles.map(async (file, index) => {
        const options = {
          maxSizeMB: 5,
          maxWidthOrHeight: 1920,
          useWebWorker: true,
        };
        const compressedFile = await imageCompression(file, options);
        return {
          id: index + Date.now(), // 用时间戳确保id唯一
          src: URL.createObjectURL(compressedFile), // 用于页面预览
          file: compressedFile, // 存储实际文件对象,用于后续上传
          name: file.name
        };
      })
    );

    // 更新本地state与Redux
    const updatedFiles = [...files, ...compressedFiles];
    setFiles(updatedFiles);
    dispatch(setImages(updatedFiles)); // 假设你的Redux action为setImages
  },
});

2. 修正Firebase上传逻辑:先上传文件,再获取下载链接

调整上传代码流程:先将文件上传至Storage,收集下载链接后,再写入Firestore:

const storage = getStorage();
const db = getFirestore();

const createDocument = async () => {
  if (!session?.user?.email) return;

  // 批量上传图片到Storage,同时获取下载链接
  const uploadPromises = images.map(async (imgItem) => {
    // 生成唯一文件名,避免覆盖已有文件
    const fileName = `livingImages/${Date.now()}_${imgItem.name}`;
    const storageRef = ref(storage, fileName);
    // 执行文件上传
    await uploadBytes(storageRef, imgItem.file);
    // 返回该文件的下载链接
    return getDownloadURL(storageRef);
  });

  // 等待所有上传完成,收集所有下载链接
  const imgURL = await Promise.all(uploadPromises);

  // 将数据写入Firestore
  const docRef = doc(db, "livingPosts", session.user.email);
  const colRef = collection(docRef, "posts");
  await addDoc(colRef, {
    name: "test upload",
    images: imgURL,
  });

  alert("上传成功");
};

关键注意点

  • 禁止用Blob URL作为Storage文件路径:Blob URL是浏览器本地临时生成的标识,Firebase无法识别,必须使用实际的File/Blob对象完成上传。
  • 正确处理异步操作:用Promise.all批量处理上传任务,确保所有文件上传完成后再写入Firestore,避免imgURL为空。
  • 文件名需唯一:用时间戳+原始文件名命名,防止同一文件名覆盖历史文件。
  • Redux存储内容:需同时存储用于预览的Blob URL和用于上传的File对象,两者缺一不可。

内容的提问来源于stack exchange,提问作者Mohammed Bekele

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:01:27