使用Facebook Graph API v23.0上传视频时通过fetch调用<UPLOAD_SESSION_ID>出现CORS错误
我之前也遇到过几乎一模一样的问题——Postman里测试一切顺畅,一到浏览器用fetch就炸CORS,太头疼了!咱们先理清楚为啥会这样:Postman是服务器端的请求工具,不受浏览器同源策略的限制,但浏览器端的fetch请求必须遵守CORS规则,Facebook的API服务器只会允许预先在应用后台配置过的合法域名发起请求,而且你代码里还有几个小问题直接加重了这个情况。
先搞懂核心问题:CORS错误的本质
Facebook的Graph API针对浏览器端的请求有严格的Origin校验,你手动设置Origin: ''不仅没用,反而会让服务器觉得这个请求不合法——浏览器会自动发送当前页面的真实Origin,而如果这个Origin没在你的Facebook应用后台登记过,服务器就会直接拒绝,抛出CORS错误。另外,你代码里的body: '@FILE_SRC'是Postman专属的语法,浏览器根本认不得这是什么东西。
解决方案1:最安全可靠——通过后端代理上传(强烈推荐)
直接在浏览器端调用Facebook的上传接口不仅有CORS问题,还会暴露你的Access Token——这玩意儿要是被用户在开发者工具里扒走,你的Page权限就彻底没了!正确的做法是让你的后端服务器当中间代理:浏览器把文件传给你的后端,后端再调用Facebook的API上传,后端请求不受CORS限制,还能保护Token不泄露。
举个简单的Node.js/Express代理例子:
// 后端代码(Node.js + Express + multer) const express = require('express'); const fetch = require('node-fetch'); const multer = require('multer'); const app = express(); const upload = multer(); // 处理文件上传 // 代理上传接口 app.post('/proxy-facebook-upload', upload.single('video'), async (req, res) => { const { uploadSessionId, accessToken, sig, fileOffset } = req.body; const videoFile = req.file; try { const fbResponse = await fetch( `https://graph.facebook.com/v23.0/upload:${uploadSessionId}?sig=${sig}`, { method: 'POST', headers: { 'Authorization': `OAuth ${accessToken}`, 'file_offset': fileOffset, 'Content-Type': videoFile.mimetype }, body: videoFile.buffer } ); const result = await fbResponse.json(); res.json(result); } catch (err) { res.status(500).json({ error: err.message }); } }); app.listen(3001, () => console.log('代理服务器跑在3001端口啦'));
然后浏览器端只需要把文件传给自己的后端:
// 前端代码 const videoInput = document.querySelector('input[type="file"]'); videoInput.addEventListener('change', async () => { const file = videoInput.files[0]; const formData = new FormData(); formData.append('video', file); formData.append('uploadSessionId', '<你的上传会话ID>'); // 注意:Access Token要存在后端,别在前端传!这里只是示意参数 formData.append('accessToken', '<后端存储的合法Token>'); formData.append('sig', '<你的请求签名>'); formData.append('fileOffset', '0'); try { const response = await fetch('/proxy-facebook-upload', { method: 'POST', body: formData }); const result = await response.json(); console.log('上传进度:', result); } catch (err) { console.error('上传失败:', err); } });
解决方案2:非要在浏览器端试?那得先改这几个地方(不推荐)
如果你只是想快速测试,非要在浏览器端直接调用,那得先把代码里的坑填上:
- 删掉手动设置的
Origin头:浏览器会自动发送当前页面的Origin,你手动设空会被浏览器忽略,反而触发服务器的安全校验。 - 把
body: '@FILE_SRC'换成真实的File/Blob对象:这是Postman的写法,浏览器里要直接传用户选择的文件或者Blob。 - 在Facebook应用后台登记你的域名:
- 打开Facebook开发者后台,找到你的应用
- 进入「设置」>「基本」,把你的网站域名添加到「网站域」里
- 再到「产品」>「Facebook登录」>「设置」,把域名添加到「有效的OAuth重定向URI」里
修正后的前端代码:
// 前端测试代码(仅用于测试,生产环境别这么干!) const videoInput = document.querySelector('input[type="file"]'); videoInput.addEventListener('change', async () => { const file = videoInput.files[0]; const uploadSessionId = '<你的上传会话ID>'; const accessToken = '<你的Access Token>'; const sig = '<你的请求签名>'; try { const response = await fetch( `https://graph.facebook.com/v23.0/upload:${uploadSessionId}?sig=${sig}`, { method: 'POST', headers: { 'Authorization': `OAuth ${accessToken}`, 'file_offset': '0', // 后续上传分段要更新这个值 'Content-Type': file.type }, body: file // 直接传File对象 } ); const result = await response.json(); console.log('上传响应:', result); } catch (err) { console.error('CORS或其他错误:', err); } });
最后再啰嗦一句
生产环境一定要用后端代理!不仅能解决CORS问题,还能避免Access Token泄露的致命风险——我之前就是图省事在前端直接传Token,结果被人薅了Page权限,折腾了好几天才找回来,血的教训啊!
内容来源于stack exchange

