Next.js 13中使用Firebase上传图片数组并存储URL到Firestore
实现Next.js 13 + Firebase:图片数组上传至Storage并将URL存入Firestore
问题背景
我正在使用Next.js 13和firebase@latest,已实现可接收图片数组的拖拽区域。现需将该图片数组上传至Firebase Storage,并将生成的图片URL数组存储到Firestore的images字段中。
当前向Firestore推送数据的代码:
import { db } from "../../firebase"; import { collection, doc, addDoc } from "firebase/firestore/lite"; import { useSession } from "next-auth/react"; export default function Home() { const { data: session } = useSession(); const createDocument = () => { const docRef = doc(db, "allPosts", session?.user?.email); const colRef = collection(docRef, "posts") addDoc(colRef, { name: name, desc: desc, images: images }); }; }
我的图片数组结构:
[ { id: 1, name: "image_processing20220628-4591-yzir35.png", src: "blob:http://localhost:3000/6e2f33e5-a749-4e9a-b502-d20b8e3f38ca" } // 更多图片项 ]
实现方案
1. 补充Firebase Storage配置
首先在Firebase配置文件(如firebase.js)中初始化并导出Storage实例:
// firebase.js import { initializeApp } from "firebase/app"; import { getFirestore } from "firebase/firestore/lite"; import { getStorage } from "firebase/storage"; const firebaseConfig = { // 你的Firebase项目配置 }; const app = initializeApp(firebaseConfig); export const db = getFirestore(app); export const storage = getStorage(app); // 新增Storage实例导出
在组件中导入Storage相关方法:
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
2. 编写图片上传工具函数
实现两个核心工具函数:将blob链接转为File对象、单张图片上传至Storage并返回下载URL:
// 将blob链接转为File对象 const blobToFile = async (blobUrl, fileName) => { const response = await fetch(blobUrl); const blob = await response.blob(); return new File([blob], fileName, { type: blob.type }); }; // 上传单张图片到Storage并返回下载URL const uploadImageToStorage = async (image) => { // 构建唯一存储路径,避免文件覆盖 const storageRef = ref(storage, `posts/${session.user.email}/${image.name}`); const file = await blobToFile(image.src, image.name); await uploadBytes(storageRef, file); return getDownloadURL(storageRef); };
3. 修改createDocument函数,先传图再存Firestore
更新原有函数,先批量上传所有图片获取URL数组,再将完整数据写入Firestore:
const createDocument = async () => { if (!session?.user?.email) { console.error("用户未登录"); return; } try { // 批量上传所有图片,等待全部完成得到URL数组 const imageUrls = await Promise.all(images.map(uploadImageToStorage)); // 写入Firestore const userPostsRef = collection(doc(db, "allPosts", session.user.email), "posts"); await addDoc(userPostsRef, { name, desc, images: imageUrls, createdAt: new Date() // 可选:添加创建时间字段 }); // 上传完成后重置表单(可选) setName(""); setDesc(""); setImages([]); } catch (err) { console.error("操作失败:", err); } };
4. 完整组件代码示例
整合后的完整组件代码:
import { useState } from "react"; import { db, storage } from "../../firebase"; import { collection, doc, addDoc } from "firebase/firestore/lite"; import { ref, uploadBytes, getDownloadURL } from "firebase/storage"; import { useSession } from "next-auth/react"; export default function Home() { const { data: session } = useSession(); const [name, setName] = useState(""); const [desc, setDesc] = useState(""); const [images, setImages] = useState([]); const blobToFile = async (blobUrl, fileName) => { const response = await fetch(blobUrl); const blob = await response.blob(); return new File([blob], fileName, { type: blob.type }); }; const uploadImageToStorage = async (image) => { const storageRef = ref(storage, `posts/${session.user.email}/${image.name}`); const file = await blobToFile(image.src, image.name); await uploadBytes(storageRef, file); return getDownloadURL(storageRef); }; const createDocument = async () => { if (!session?.user?.email) { console.error("用户未登录"); return; } try { const imageUrls = await Promise.all(images.map(uploadImageToStorage)); const userPostsRef = collection(doc(db, "allPosts", session.user.email), "posts"); await addDoc(userPostsRef, { name, desc, images: imageUrls, createdAt: new Date() }); setName(""); setDesc(""); setImages([]); } catch (err) { console.error("操作失败:", err); } }; return ( <div> <input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder="帖子标题" /> <textarea value={desc} onChange={(e) => setDesc(e.target.value)} placeholder="帖子描述" /> {/* 你的拖拽区域组件 */} <button onClick={createDocument}>发布帖子</button> </div> ); }
内容的提问来源于stack exchange,提问作者Mohammed Bekele
相关产品推荐
相关产品推荐

