React上传MP4至Express再转存Abyss云存储请求异常排查
问题描述
我找到一个视频云存储网站Abyss,它提供的上传文档是cURL形式:
curl -F "file=@demo.mp4" up.hydrax.net/MY_API_KEY
我尝试用React+ExpressJS实现这个功能,React端提交视频的代码如下:
const handleSubmit = async () => { let formData = new FormData() formData.append("file", selectVideo) await axios.post( `/film/add-episode/${_id}`, { formData, }, { headers: { "content-type": "multipart/form-data", }, } ) }
视频会发送到Express的POST路由,后端控制器代码如下:
const filmController = { addEpisode: async (req, res) => { const { formData } = req.body try { console.log(formData) // 从React传至Express的是FormData对象 //API URL to upload video const response = await axios.post( "http://up.hydrax.net/MY_API_KEY_HERE", { formData, }, { headers: { "content-type": "multipart/form-data", }, } ) console.log(response) res.json({ msg: "Success add episode" }) } catch (err) { return res.status(500).json({ msg: err.message }) } }, }
我需要在后端通过axios获取Abyss上传成功后生成的slug并存入MongoDB,但现在请求一直处于pending状态,文件也没上传到云存储,怀疑是FormData对象无法被识别为MP4文件。
问题原因及修复方案
你的代码存在两个核心错误,导致上传失败:
1. React端:FormData被错误包裹成普通对象发送
你在axios请求里把formData放在了一个对象中传递,这会破坏FormData的原始结构,Express无法正确解析文件。正确做法是直接把FormData作为请求体发送,且无需手动设置multipart/form-data头——axios会自动生成包含boundary的正确Content-Type。
修正后的React代码:
const handleSubmit = async () => { let formData = new FormData() formData.append("file", selectVideo) await axios.post( `/film/add-episode/${_id}`, formData // 直接传递FormData,不要包裹成对象 ) }
2. Express端:未正确解析multipart请求,且转发时错误处理FormData
Express默认不支持解析multipart/form-data格式的请求,需要借助multer中间件处理文件上传;同时后端转发请求时,错误地将FormData包裹成对象,导致第三方API无法识别文件。
步骤1:安装multer
npm install multer
步骤2:配置Express路由使用multer
在路由文件中添加multer配置,用内存存储临时保存文件(适合转发到第三方API):
const multer = require('multer'); const upload = multer({ storage: multer.memoryStorage() }); // 路由中使用upload.single('file')解析单个文件 router.post('/film/add-episode/:_id', upload.single('file'), filmController.addEpisode);
步骤3:修正后端控制器代码
multer会将解析后的文件存入req.file,你需要基于这个文件对象重新构建FormData,再发送到Abyss的API:
const filmController = { addEpisode: async (req, res) => { try { // 从multer解析结果中获取文件信息 const { originalname, buffer, mimetype } = req.file; // 构建用于上传到Abyss的FormData const uploadFormData = new FormData(); uploadFormData.append('file', buffer, { filename: originalname, contentType: mimetype }); // 发送请求到Abyss API const response = await axios.post( "http://up.hydrax.net/MY_API_KEY_HERE", uploadFormData, // 直接传递FormData { headers: { ...uploadFormData.getHeaders() // 自动生成正确的Content-Type } } ); // 获取slug并存入MongoDB(根据实际模型调整) const slug = response.data.slug; // await FilmModel.updateOne({ _id: req.params._id }, { $push: { episodes: slug } }) res.json({ msg: "Success add episode", slug }); } catch (err) { console.error(err); return res.status(500).json({ msg: err.message }); } }, }
额外注意事项
- 确保
selectVideo是通过<input type="file"/>获取到的File对象,而非其他类型。 - 需根据Abyss官方文档确认API返回结构,调整
response.data.slug的取值路径。 - 若上传大文件,内存存储可能不足,可改用multer的磁盘存储,读取文件流后再转发。
内容的提问来源于stack exchange,提问作者Bunny
相关产品推荐
相关产品推荐

