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

如何在React中实现Cloudinary视频上传并嵌入帖子?

用户视频上传与帖子嵌入功能实现问题

我正在开发网站,需实现用户上传视频片段并嵌入到发布帖子的功能。目前已创建视频专属Schema、路由及控制器,可通过Postman将视频上传至Cloudinary并将链接存储到MongoDB集合,但无法编写React代码实现用户端的视频上传与帖子嵌入。当前图片上传功能正常,尝试用相同方法实现视频上传却未成功。

现有代码

1. Postman视频上传控制器函数

const uploadVideo = async (req, res) => {
       try {
           cloudinary.uploader.upload(req.file.path, 
           {
            resource_type: "video",
            folder: "video",
          },
          async (err, result) => {
          if (err) {
             console.log(err)
             return res.status(500).json("error in upload video function!"+err)
          }
          const upload = new Upload({
            name: req.file.originalname,
            url: result.url,
            cloudinary_id: result.public_id
          });
          await upload.save();
          res.status(200).json(upload)
       })
       } catch (error) {
           console.log(error)
       }
       
       } 

2. 创建帖子控制器函数(新增视频逻辑未生效)

const createPost = async(req, res) => {
    try {
        const {postedBy, text} = req.body;
        let {img} = req.body;
        let {video} = req.body;

        if(!postedBy || !text) {
            return res.status(400).json({ error: "PostedBy and text fields are required!" })
        }

        const user = await User.findById(postedBy);
        if(!user) {
            return res.status(404).json({ error: "User not found!" })
        }

        if(user._id.toString() !== req.user._id.toString()){
            return res.status(401).json({ error: "Unauthorized to create post" })
        }

        const maxLength = 500;
        if(text.length > maxLength){
            return res.status(400).json({ error: `Text must be less than ${maxLength} characters!` })
        }

        if(img){
            const uploadedResponse = await cloudinary.uploader.upload(img);
            img = uploadedResponse.secure_url;
        }

        if(video){ // <= this if statment that i added
            try {
                cloudinary.uploader.upload(req.file.path, 
                {
                    resource_type: "video",
                    folder: "video",
                },
                async (err, result) => {
                if (err) {
                    console.log("error in video if statment! " + err)
                    return res.status(500).json("error in upload video function!"+err)
                }
                const upload = new Upload({
                    name: req.file.originalname,
                    url: result.url,
                    cloudinary_id: result.public_id
                });
                await upload.save();
                // res.status(200).json(upload)
            })
            } catch (error) {
                console.log("error in video catch " + error)
            }
        }

        const newPost = new Post({ postedBy, text, img, video })

        await newPost.save();
        res.status(201).json( newPost )

    } catch (err) {
        res.status(500).json({ error: err.message });
        console.log(err)
    }
};

3. React创建帖子函数(新增video参数未生效)

const handleCreatePost = async() => {
 setLoading(true)
  try {
 const res = await fetch("/api/posts/create", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({postedBy: user._id, text: postText, img: 
imgUrl, video: videoUrl}) // <= i added "video: videoUrl" to make it accept the video in newPost but it did't work
  })
   
   const data = await res.json();
   if(data.error) {
    console.log("2"+data.error)
    showToast("Error", data.error, "error")
    return;
   }
   showToast("Success", "Post created successfully", "success");
   if(username === user.username) {
   setPosts([data, ...posts]);
   }
   onClose();
   setPostText("");
   setImgUrl("");
 } catch (error) {
   console.log("3"+error)
   showToast("Error", error, "error");
 } finally {
   setLoading(false);
 }
};

需要解决的问题

  1. 实现用户可选上传视频并正确嵌入到帖子中的功能;
  2. 能否不创建单独的视频Schema来实现该功能;
  3. 能否在React函数中通过.then()调用两个API。

解决方案

1. 实现用户端视频上传与帖子嵌入

核心问题是前端传递视频的方式错误,以及后端视频处理逻辑存在时序问题,以下是修正方案:

前端修改(React)

用户上传的本地视频需要用FormData传递,不能直接传JSON。同时需要添加视频选择输入:

// 组件中添加视频选择输入
<input 
  type="file" 
  accept="video/*" 
  onChange={(e) => setVideoFile(e.target.files[0])}
/>

修改创建帖子函数:

const handleCreatePost = async() => {
  setLoading(true);
  try {
    const formData = new FormData();
    formData.append('postedBy', user._id);
    formData.append('text', postText);
    if (imgFile) formData.append('img', imgFile); // 改为传递文件对象,而非URL
    if (videoFile) formData.append('video', videoFile);

    const res = await fetch("/api/posts/create", {
      method: "POST",
      headers: {
        'Authorization': `Bearer ${yourAuthToken}` // 如有认证需求添加
      },
      body: formData // 不手动设置Content-Type,浏览器自动处理multipart/form-data
    });

    const data = await res.json();
    if(data.error) {
      console.log(data.error);
      showToast("Error", data.error, "error");
      return;
    }
    showToast("Success", "Post created successfully", "success");
    if(username === user.username) {
      setPosts([data, ...posts]);
    }
    onClose();
    setPostText("");
    setImgFile(null);
    setVideoFile(null);
  } catch (error) {
    console.log(error);
    showToast("Error", error.message, "error");
  } finally {
    setLoading(false);
  }
};

后端修改(创建帖子控制器)

改用Cloudinary异步上传API,避免回调导致的时序问题,同时用multer处理多文件上传:

const createPost = async(req, res) => {
  try {
    const {postedBy, text} = req.body;
    let img = null;
    let video = null;

    if(!postedBy || !text) {
      return res.status(400).json({ error: "PostedBy and text fields are required!" })
    }

    const user = await User.findById(postedBy);
    if(!user) {
      return res.status(404).json({ error: "User not found!" })
    }

    if(user._id.toString() !== req.user._id.toString()){
      return res.status(401).json({ error: "Unauthorized to create post" })
    }

    const maxLength = 500;
    if(text.length > maxLength){
      return res.status(400).json({ error: `Text must be less than ${maxLength} characters!` })
    }

    // 处理图片上传
    if(req.files?.img) {
      const uploadedResponse = await cloudinary.uploader.upload(req.files.img[0].path);
      img = uploadedResponse.secure_url;
    }

    // 处理视频上传
    if(req.files?.video) {
      const uploadedResponse = await cloudinary.uploader.upload(req.files.video[0].path, {
        resource_type: "video",
        folder: "video",
      });
      video = uploadedResponse.secure_url;
    }

    const newPost = new Post({ postedBy, text, img, video });
    await newPost.save();
    res.status(201).json(newPost);

  } catch (err) {
    res.status(500).json({ error: err.message });
    console.log(err);
  }
};

配置multer支持多字段上传:

const storage = multer.diskStorage({/* 你的存储配置 */});
const upload = multer({ storage });
// 路由中使用
router.post('/create', upload.fields([{ name: 'img', maxCount: 1 }, { name: 'video', maxCount: 1 }]), createPost);

2. 无需单独视频Schema的实现方式

完全可以不需要单独的Upload Schema,直接在Post Schema中添加视频字段存储URL:

const postSchema = new mongoose.Schema({
  postedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
  text: { type: String, required: true, maxlength: 500 },
  img: { type: String }, // 存储图片URL
  video: { type: String }, // 存储视频URL
  // 其他现有字段
}, { timestamps: true });

这样可以直接将视频URL存在帖子文档中,简化数据结构和查询逻辑,无需维护额外的Upload集合。

3. React中用.then()调用两个API

可以通过链式调用.then()实现分步操作,比如先上传视频,再创建帖子:

const handleCreatePost = () => {
  setLoading(true);
  // 第一步:上传视频(如果有)
  const uploadVideoTask = videoFile 
    ? fetch('/api/upload/video', {
        method: 'POST',
        body: new FormData().append('video', videoFile)
      }).then(res => res.json())
    : Promise.resolve(null);

  uploadVideoTask
    // 第二步:创建帖子
    .then(videoData => {
      const formData = new FormData();
      formData.append('postedBy', user._id);
      formData.append('text', postText);
      if (imgFile) formData.append('img', imgFile);
      if (videoData) formData.append('video', videoData.url);

      return fetch("/api/posts/create", {
        method: "POST",
        body: formData
      });
    })
    .then(res => res.json())
    .then(data => {
      if(data.error) {
        showToast("Error", data.error, "error");
        return;
      }
      showToast("Success", "Post created successfully", "success");
      if(username === user.username) {
        setPosts([data, ...posts]);
      }
      onClose();
      setPostText("");
      setImgFile(null);
      setVideoFile(null);
    })
    .catch(error => {
      console.log(error);
      showToast("Error", error.message, "error");
    })
    .finally(() => {
      setLoading(false);
    });
};

这种方式适合需要确保视频上传成功后再创建帖子的场景。


内容的提问来源于stack exchange,提问作者khayal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:03:13