Next.js上传图片至Firebase Storage失败问题求助
尝试从 Next.js 应用向 Firebase Storage 上传图片,已配置环境变量且 Firebase 初始化代码无误,Storage 规则已关闭身份验证检查,但上传时触发错误:
Error uploading image-filename.jpg
Firebase Storage: An unknown error occurred, please check the error payload for server response. (storage/unknown)
浏览器控制台显示无效请求返回 412 错误:
https://firebasestorage.googleapis.com/v0/b/firebase-storage-bucket/o?name=images%image-filename.jpg 412 (Precondition Failed)
相关代码及配置如下:
Firebase 初始化代码
// Import the functions you need from the SDKs you need import { initializeApp, getApps } from "firebase/app"; import { getAnalytics } from "firebase/analytics"; import { getAuth } from "firebase/auth"; import { getFirestore } from "firebase/firestore"; import { getStorage } from "firebase/storage"; // Your web app's Firebase configuration const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID, }; // Initialize Firebase let firebaseApp; if (!getApps().length) { firebaseApp = initializeApp(firebaseConfig); } // Initialize Firebase services const auth = getAuth(firebaseApp); const db = getFirestore(firebaseApp); const STORAGE_FOLDER_PATH = `gs://${process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET}`; const storage = getStorage(firebaseApp, STORAGE_FOLDER_PATH); // Initialize Analytics and get analytics instance let analytics; if (typeof window !== "undefined") { analytics = getAnalytics(firebaseApp); } export { firebaseApp, auth, db, storage, analytics };
上传组件核心代码
const handleUpload = async () => { if (imageFiles.length === 0) { //handle condition if no image is selected } setIsUploading(true); const uploadPromises = imageFiles.map(async ({ file }) => { const filename = nanoid(); const fileExt = file.name.split(".").pop(); const storageRef = fireRef( storage, `images/${filename}.${fileExt}` ); try{ const res = await uploadBytes(storageRef, file); return res.metadata.fullPath; } catch (error) { toast.error(`Error uploading ${file.name}`, { description: error.message, }); return null; } }); try { const uploadedUrls = await Promise.all(uploadPromises); const validUrls = uploadedUrls.filter(url => url !== null); if (validUrls.length > 0) { toast.success(`Successfully uploaded ${validUrls.length} image${validUrls.length === 1 ? '' : 's'}`); } setIsUploading(false); return validUrls; } catch (error) { toast.error("Error uploading images", { description: error.message, }); setIsUploading(false); return []; } };
Storage 规则配置
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /images/{imageId} { allow read: if true; allow write: if true; } match /users/{userId}/{allPaths=**} { allow read: if true; allow write: if true; } } }
核心原因
初始化 Storage 时错误传入了完整的 gs:// 格式路径作为存储桶 URL,导致后续生成文件引用时路径拼接异常,出现无效的请求路径(如 images%image-filename.jpg 缺少路径分隔符),触发 412 预条件失败。
修复步骤
修正 Storage 初始化代码
移除手动拼接的gs://路径,getStorage会自动读取firebaseConfig中的storageBucket值,无需额外传入存储桶 URL:// 删除错误的路径定义 // const STORAGE_FOLDER_PATH = `gs://${process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET}`; // 直接初始化 storage const storage = getStorage(firebaseApp);验证路径拼接逻辑
上传时的文件路径(images/${filename}.${fileExt})本身逻辑正确,修正 Storage 初始化后,会自动生成有效的相对路径,请求 URL 格式将恢复正常。重新测试上传
重启应用后尝试上传图片,412 错误即可解决。
内容的提问来源于stack exchange,提问作者Petrando Richard

