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
相关产品推荐
相关产品推荐

