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




