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

为何Safari向Node.js API POST上传react-video-recorder录制视频时偶发CORS错误

Safari仅上传react-video-recorder录制视频时报CORS错误问题

我们团队正在开发一款网站,支持用户上传已有视频或在浏览器内录制新视频文件。本地文件上传通过标准HTML表单实现,录制功能基于react-video-recorder开发。两种场景下文件准备完成后,均会通过ky发起POST请求提交表单,代码如下:

const response = await ky.post(`https://xxx.xxx.xxx.xxx:xxxx/upload`,
        {
          timeout: false,
          body: formData, 
          
          headers: {
            'Authorization': `Bearer ${sessionStorage.getItem('token')}`
          }
        }).json();

两种添加文件的方式调用同一个提交函数,因此无论用户通过哪种方式获取视频,POST请求的请求头完全一致。

但实际测试发现,仅当上传react-video-recorder录制的视频时,Safari会抛出CORS错误,而同页面在Mac端Chrome上执行录制上传操作则不会出现该问题。

已核对服务端配置,明确设置了允许对应站点的跨域头,为避免拼写错误,甚至试过直接将请求的origin头值赋值给Access-Control-Allow-Origin,服务端核心代码如下:

app.post('/upload', 
    passport.authenticate('jwt', {session: false}),
    function(req,res) {
        res.setHeader('Access-Control-Allow-Origin','https://my-website.com')
        res.setHeader('Access-Control-Allow-Methods','POST, GET, OPTIONS')
        // 业务逻辑
        ...
})

目前怀疑这根本不是CORS问题,可能是其他环节出错,但Safari给出的错误提示对排查没有帮助。需要排查思路或解决方案。

补充说明:已导出两次请求的HTTP报文,发现二者存在差异,但不清楚出现差异的原因:
成功请求的报文如下:

POST /upload HTTP/1.1
Accept: application/json
Origin: https://my-website.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryGRcSY52tKpQVBLIb
Authorization: Bearer _redacted
Referer: https://my-website.com/
Content-Length: 51199
Host: my-api.com:3004
Accept-Language: en-ca
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

失败请求的报文如下:

POST /upload
Authorization: Bearer _redacted
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0Uzp1dEaDyHuIhEw
Accept: application/json
Origin: https://my-website.com
Referer: https://my-website.com/Upload
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15

疑问:这是否说明录制的视频文件没有成功挂载到POST请求中?


问题解答

首先可以明确:失败请求报文缺失了Host、Content-Length、HTTP/1.1版本声明等核心字段,并非录制文件未挂载到请求,而是Safari在处理特殊格式的Blob/File类型表单字段时触发了底层请求构造异常,看起来的CORS错误本质是请求未正确发送到服务端,服务端自然不会返回合法跨域头,最终Safari抛出跨域错误。

排查和解决方案可按以下顺序执行:

  • 调整录制输出格式:react-video-recorder默认输出webm格式视频,Safari对webm格式的录制和读取兼容性极差,生成的Blob头部存在异常会导致表单构造失败。可手动指定录制输出为mp4格式,验证问题是否消失。
  • 修复服务端跨域配置:你当前的跨域头仅在POST路由的业务逻辑中返回,而跨域POST请求前浏览器会先发送OPTIONS预检请求,你的服务端未处理预检请求的跨域头返回,Safari对预检请求的校验严格度远高于Chrome,这是触发CORS报错的核心诱因之一。需要把跨域头设置提到所有路由之前,单独处理OPTIONS请求直接返回204状态码,示例代码如下:
// 所有请求前先设置跨域头
app.use(function(req, res, next) {
  res.setHeader('Access-Control-Allow-Origin','https://my-website.com')
  res.setHeader('Access-Control-Allow-Methods','POST, GET, OPTIONS')
  res.setHeader('Access-Control-Allow-Headers','Content-Type, Authorization')
  if (req.method === 'OPTIONS') {
    return res.sendStatus(204)
  }
  next()
})
  • 优化ky请求配置:不要手动指定Content-Type头,让浏览器自动处理multipart/form-data的boundary生成;同时添加retry: 0配置,避免Safari的失败重试触发重复跨域校验。
  • 验证表单合法性:发起请求前打印formData的所有字段,确认录制生成的File对象已正确append到formData中,同时检查File对象的size、name属性是否正常,Safari下未正常结束录制生成的文件size会为0,也会导致请求构造失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 00:24:03