如何在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); } };
需要解决的问题
- 实现用户可选上传视频并正确嵌入到帖子中的功能;
- 能否不创建单独的视频Schema来实现该功能;
- 能否在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
相关产品推荐
相关产品推荐

