Firebase Storage上传图片获URL存MongoDB异步问题求助
问题描述
我正在开发一款发帖应用,用户可发布图片和文字。需求是:将图片存储到Firebase Storage存储桶,立即获取图片链接,再把图片链接和照片描述文本存入MongoDB数据库。技术栈为Node.js、Express和MongoDB。
当前遇到的问题:Promise总是返回空的图片URL数组,向数据库发送空数组后,URL才会返回。需要调整执行顺序,确保在存入数据库前获取到所有图片URL。
原代码
import ModalStyles from "./_createpost.module.scss"; import { ref, uploadBytes, getDownloadURL } from "firebase/storage"; import { storage } from "../../../config/firebaseConfig"; import { faCamera, faPhotoFilm } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useContext, useEffect, useState } from "react"; import UserContext from "../../../context/userContext"; import { uuid } from "uuidv4"; import { v4 } from "uuid"; import axios from "axios"; interface IModalProps { } const Modal = ({ }: IModalProps) => { const user = useContext(UserContext) const [imageUpload, setImageUpload] = useState<FileList | null>({} as FileList); const [postDesc, setPostDesc] = useState<string>(''); const [imageUrls, setImageUrls] = useState<Array<string>>([]); const convertToBlob = (file: File): Promise<Blob> => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => { const blob = new Blob([reader.result as ArrayBuffer], { type: file.type }); resolve(blob); }; reader.onerror = reject; reader.readAsArrayBuffer(file); }); }; const uploadImages = async () => { if (!imageUpload) return; let promises: string[] = []; for (let index = 0; index < imageUpload.length; index++) { const blob = await convertToBlob(imageUpload[index]); const imageRef = ref(storage, `images/${imageUpload[index].name + v4()}`); uploadBytes(imageRef, blob).then((snapshot) => { getDownloadURL(snapshot.ref).then((urls) => { console.log(urls); promises.push(urls) setImageUrls(prev => [...prev, urls]); }); }).catch(err => console.log('An Error Occured!', err)) } return promises; }; const uploadFile = async (e: React.FormEvent) => { e.preventDefault(); const imgs = uploadImages(); console.log(imgs); if (!imgs) return; try { const response = await axios.post(`http://localhost:5000/post/create`, { creator_id: "64af540d576b8737651135a4", post_description: postDesc, photos: imgs }); if (response.data) console.log('image uploaded successfully'); } catch (error) { console.log('An Error occured while uploading the post'); } } const onchange = (event: React.ChangeEvent<HTMLInputElement>) => { const files = event.target.files; setImageUpload(files); } return <div className={ModalStyles.modal__container}> <h4 className={ModalStyles.modal__title}>Create Post</h4> <div className={`divider ${ModalStyles.modal__divider}`}></div> <div> <div className={`${ModalStyles.post__post_header} mt-1`}> <img src={user.profilePixs} className={ModalStyles.post__user_pixs} alt={user.firstname} /> <div className={ModalStyles.post__post_info}> <p className={ModalStyles.post__user_name}>{user.firstname} {user.lastname}</p> <p>Public</p> </div> </div> <form className={ModalStyles.modal__post_inputField} onSubmit={uploadFile}> <textarea name="post_desc" id="post_desc" className={ModalStyles.modal__description} value={postDesc} onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setPostDesc(e.target.value)}></textarea> <div className={`divider ${ModalStyles.modal__divider}`}></div> <p className={ModalStyles.modal__addPhotoText}> <span> Add to your Post</span> <FontAwesomeIcon icon={faPhotoFilm} size="2xl" className={ModalStyles.modal__addPhotoIcon} /> <input type="file" name="photo" id="photo" multiple onChange={onchange} accept="image/png, image/jpeg" className={ModalStyles.modal__addPhotoIcon} /> </p> <input type="submit" value="Post" className={ModalStyles.modal__submitBtn} /> </form> </div> </div>; }; export default Modal;
解决方案
问题核心是异步操作未正确等待:原代码中uploadImages函数里的uploadBytes和getDownloadURL是异步执行的,函数直接返回了空数组;同时uploadFile调用uploadImages时没有等待其完成,导致发送请求时URL还未生成。
修改步骤:
重构
uploadImages函数,用Promise.all等待所有上传完成- 把每个上传+获取URL的操作包装成Promise,存入数组
- 用
Promise.all等待所有异步操作完成,返回完整的URL数组 - 移除多余的
convertToBlob函数:Firebase的uploadBytes可直接接受File对象,无需转Blob
在
uploadFile中等待uploadImages完成- 调用
uploadImages时加await,确保拿到解析后的URL数组再发送请求
- 调用
修改后的关键代码:
重构后的uploadImages函数
const uploadImages = async () => { if (!imageUpload) return []; // 将FileList转为数组,遍历生成每个文件的上传Promise const uploadPromises = Array.from(imageUpload).map(async (file) => { const imageRef = ref(storage, `images/${file.name + v4()}`); // 直接上传File对象,无需转Blob const snapshot = await uploadBytes(imageRef, file); const url = await getDownloadURL(snapshot.ref); return url; }); // 等待所有上传操作完成,获取全部URL const urls = await Promise.all(uploadPromises); setImageUrls(urls); return urls; };
修改后的uploadFile函数
const uploadFile = async (e: React.FormEvent) => { e.preventDefault(); // 等待uploadImages完成,拿到实际的URL数组 const imgs = await uploadImages(); // 检查是否有上传成功的图片 if (!imgs.length) return; try { await axios.post(`http://localhost:5000/post/create`, { creator_id: "64af540d576b8737651135a4", post_description: postDesc, photos: imgs }); console.log('image uploaded successfully'); } catch (error) { // 打印具体错误便于排查 console.error('An Error occured while uploading the post', error); } };
额外优化
- 删除无用的
convertToBlob函数,简化代码 - 错误处理中打印具体错误信息,方便排查问题
- 检查
imgs数组长度而非是否为null,逻辑更严谨
内容的提问来源于stack exchange,提问作者Orisfina
相关产品推荐
相关产品推荐

