You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用JavaScript调用YouTube Data API直接传视频遇400错误求助

求助:YouTube Data API的youtube.videos.insert上传视频报400错误(mediaBodyRequired)

我已经借助YouTube Data API文档,用JavaScript(XMLHttpRequest)实现了视频的可恢复上传,这部分文档比较完善。但我觉得youtube.videos.insert这个JavaScript函数的文档不够详尽,经过两天网络调研仍无头绪,特此求助。

我的代码示例:

function defineRequest() { 
  if (!isAuthorized) { 
    alert("You need to authorize the request to proceed."); 
    return; 
  } 
  var file = document.querySelector('input[type=file]').files[0]; 
  if (!file) { 
    alert("You need to select a file to proceed."); 
    return; 
  } 
  var reader = new FileReader(); 
  reader.onloadend = function() { 
    alert("file load completed"); 
    var data = reader.result; 
    var fileStream = new Blob([new Uint8Array(data)], {type: 'application/octet-stream'}); 
    //var fileStream = new Blob([data], {type: 'application/octet-stream'}); 
    alert(fileStream.size); 
    alert(fileStream.type); 
    var request = gapi.client.youtube.videos.insert({ 
      media: { 
        mimeType: 'application/octet-stream', 
        body: fileStream 
      }, 
      part: 'id,snippet,status', 
      notifySubscribers: true, 
      resource: { 
        snippet: { 
          categoryId: 22, 
          title: $('#title').val(), 
          description: $('#description').text(), 
          tags: ['hello'] 
        }, 
        status: { 
          privacyStatus: $('#privacy-status option:selected').text() 
        } 
      } 
    }); 
    request.execute(function (response) { 
      alert(JSON.stringify(response)); 
      console.log(JSON.stringify(response)); 
      if (response === false) { 
        alert("cannot upload video"); 
        console.log("Cannot upload video"); 
        return; 
      } else { 
        if (response.code != 0) { 
          alert("cannot upload video"); 
          console.log("Cannot upload video"); 
          return; 
        } 
        var result = response.result; 
        alert(JSON.stringify(result)); 
        alert("upload completed"); 
      } 
    }); 
  } 
  reader.readAsArrayBuffer(file); 
}

我收到了400错误(mediaBodyRequired),错误信息如下:

{ 
  "code":400, 
  "data":[{ 
    "domain":"youtube.video", 
    "reason":"mediaBodyRequired", 
    "message":"The request does not include the video content.", 
    "locationType":"other", 
    "location":"body" 
  }], 
  "message":"The request does not include the video content.", 
  "error": { 
    "code":400, 
    "data":[{ 
      "domain":"youtube.video", 
      "reason":"mediaBodyRequired", 
      "message":"The request does not include the video content.", 
      "locationType":"other", 
      "location":"body" 
    }], 
    "message":"The request does not include the video content." 
  } 
}

解决方案

你遇到的mediaBodyRequired错误,核心原因是gapi.client.youtube.videos.insertmedia.body参数没有被API正确识别——gapi.client的底层实现对直接传递Blob对象的支持有限,导致视频内容没有被正确发送到服务器。我给你两种可行的修正方案:

方案1:使用FormData结合gapi.client.request上传

这种方式适合小文件,直接通过多部分表单同时传递元数据和视频文件:

function defineRequest() {
  if (!isAuthorized) {
    alert("You need to authorize the request to proceed.");
    return;
  }
  
  const file = document.querySelector('input[type=file]').files[0];
  if (!file) {
    alert("You need to select a file to proceed.");
    return;
  }

  // 构建视频元数据
  const videoResource = {
    snippet: {
      categoryId: 22,
      title: $('#title').val(),
      description: $('#description').text(),
      tags: ['hello']
    },
    status: {
      privacyStatus: $('#privacy-status option:selected').text()
    }
  };

  // 手动构建上传请求
  const request = gapi.client.request({
    path: '/youtube/v3/videos',
    method: 'POST',
    params: {
      part: 'id,snippet,status',
      notifySubscribers: true
    },
    headers: {
      'Content-Type': 'multipart/form-data' // 明确使用多部分表单格式
    },
    body: new FormData()
  });

  // 向FormData添加元数据和文件
  const formData = request.body;
  formData.append('resource', JSON.stringify(videoResource));
  formData.append('media', file, file.name);

  request.execute(function(response) {
    console.log(response);
    if (response.error) {
      alert(`上传失败: ${response.error.message}`);
      console.error("上传失败详情", response.error);
      return;
    }
    alert(`上传完成!视频ID: ${response.result.id}`);
    console.log("上传结果", response.result);
  });
}

方案2:结合你已实现的可恢复上传流程

既然你已经搞定了XMLHttpRequest的可恢复上传,推荐用这种方式(YouTube官方也推荐大文件用可恢复上传),先通过gapi.client获取上传URL,再用你已有的逻辑上传:

function initResumableUpload() {
  if (!isAuthorized) {
    alert("You need to authorize the request to proceed.");
    return;
  }
  
  const file = document.querySelector('input[type=file]').files[0];
  if (!file) {
    alert("You need to select a file to proceed.");
    return;
  }

  const videoResource = {
    snippet: {
      categoryId: 22,
      title: $('#title').val(),
      description: $('#description').text(),
      tags: ['hello']
    },
    status: {
      privacyStatus: $('#privacy-status option:selected').text()
    }
  };

  // 发送初始化请求,获取可恢复上传的URL
  gapi.client.request({
    path: '/upload/youtube/v3/videos',
    method: 'POST',
    params: {
      part: 'id,snippet,status',
      uploadType: 'resumable'
    },
    headers: {
      'X-Upload-Content-Type': file.type, // 告诉API视频的MIME类型
      'X-Upload-Content-Length': file.size // 告诉API视频文件大小
    },
    body: videoResource
  }).execute(function(response) {
    if (response.error) {
      alert(`初始化上传失败: ${response.error.message}`);
      console.error("初始化失败详情", response.error);
      return;
    }
    // 拿到可恢复上传的URL
    const uploadUrl = response.headers['Location'];
    // 调用你已经实现的可恢复上传函数
    startResumableUpload(uploadUrl, file);
  });
}

// 这里放入你之前用XMLHttpRequest实现的可恢复上传逻辑
function startResumableUpload(uploadUrl, file) {
  // 你的可恢复上传代码,比如处理Range头部、进度监听、重试逻辑等
  // 示例框架:
  const xhr = new XMLHttpRequest();
  xhr.open('PUT', uploadUrl);
  xhr.setRequestHeader('Content-Type', file.type);
  
  xhr.upload.addEventListener('progress', (e) => {
    if (e.lengthComputable) {
      const progress = (e.loaded / e.total) * 100;
      console.log(`上传进度: ${progress.toFixed(2)}%`);
    }
  });
  
  xhr.addEventListener('load', () => {
    if (xhr.status === 200) {
      const response = JSON.parse(xhr.responseText);
      alert(`上传完成!视频ID: ${response.id}`);
      console.log("上传结果", response);
    } else {
      alert(`上传失败: ${xhr.statusText}`);
      console.error("上传失败", xhr.responseText);
    }
  });
  
  xhr.send(file);
}

关键说明

  1. 不要用FileReader转换文件:File对象本身就是Blob的子类,直接使用即可,避免不必要的格式转换导致的错误。
  2. 明确MIME类型:使用视频文件实际的MIME类型(比如video/mp4),而不是通用的application/octet-stream,帮助YouTube更好地识别内容。
  3. 可恢复上传更可靠:对于大文件,可恢复上传能处理网络中断等问题,这也是你已经掌握的能力,结合初始化请求就能完美解决当前问题。

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

火山引擎 最新活动