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

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还未生成。

修改步骤:

  1. 重构uploadImages函数,用Promise.all等待所有上传完成

    • 把每个上传+获取URL的操作包装成Promise,存入数组
    • 用Promise.all等待所有异步操作完成,返回完整的URL数组
    • 移除多余的convertToBlob函数:Firebase的uploadBytes可直接接受File对象,无需转Blob
  2. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 21:37:33