使用PHP 7库生成签名URL向Google Storage上传大文件时文件损坏问题求助
解决Google Storage大文件上传后损坏及Content-Type错误的问题
我来帮你排查下这个问题,你遇到的文件损坏和类型错误其实是同一个核心问题导致的,咱们一步步拆解解决:
问题根源
你当前的前端上传逻辑犯了两个关键错误:
- 用FormData包装文件上传:PUT请求直接传FormData会让请求体变成
multipart/form-data格式(包含字段名、分隔符等额外内容),而不是原始的MP4二进制数据。Google Storage存下来的是这个FormData的内容,自然无法正常播放。 - 没正确处理可恢复上传流程:后端用
beginSignedUploadSession生成的是**可恢复上传(resumable upload)**的签名URL,这种上传需要先初始化会话,不能直接PUT文件。
这两个问题叠加起来,就导致了文件损坏,同时Google Storage识别不到真实的文件类型,只能 fallback 到application/octet-stream。
解决方案
方案一:修复可恢复上传流程(适合大文件,支持断点续传)
如果你需要大文件的断点续传能力,按照以下步骤调整代码:
1. 前端代码修改(核心:移除FormData,先初始化会话再传文件)
let file = files[i]; let type = file.type || 'video/mp4'; // 兜底处理浏览器识别不准的情况 $.ajax({ type: "GET", url: "/getUrl", data: dataToPass, success: function (json) { let jsonParse = JSON.parse(json); let sessionUrl = jsonParse.url; // 第一步:初始化可恢复上传会话 $.ajax({ type: "POST", url: sessionUrl, headers: { "x-goog-resumable": "start", "Content-Type": type }, success: function (_, __, xhr) { // 从响应头获取实际的上传URL let uploadUrl = xhr.getResponseHeader('Location'); // 第二步:上传原始文件二进制内容 let uploadRequest = $.ajax({ type: 'PUT', url: uploadUrl, data: file, // 直接传文件对象,不要用FormData! processData: false, contentType: type, cache: false, xhr: function () { let myXhr = $.ajaxSettings.xhr(); if (myXhr.upload) { myXhr.upload.addEventListener('progress', thisIsAFunction, false); } return myXhr; }, success: () => console.log('文件上传成功!'), error: (xhr, status, err) => console.error('上传失败:', err) }); }, error: (xhr, status, err) => console.error('初始化会话失败:', err) }); } });
2. 后端代码确认
你的后端代码生成可恢复上传URL的逻辑没问题,但要确保$type变量是正确的video/mp4,而不是空值或错误类型。
方案二:改用普通签名上传(适合中小文件,流程更简单)
如果你的文件不需要断点续传,直接改用普通的签名PUT URL会更省心:
1. 后端代码修改
// 替换原来的beginSignedUploadSession,生成普通签名PUT URL $url = $object->signedUrl( new \DateTime('1 hour'), // URL有效期,按需调整 [ 'method' => 'PUT', 'contentType' => $type, ] );
2. 前端代码简化
let file = files[i]; let type = file.type || 'video/mp4'; $.ajax({ type: "GET", url: "/getUrl", data: dataToPass, success: function (json) { let jsonParse = JSON.parse(json); let uploadUrl = jsonParse.url; // 直接上传文件二进制 let uploadRequest = $.ajax({ type: 'PUT', url: uploadUrl, data: file, processData: false, contentType: type, cache: false, xhr: function () { let myXhr = $.ajaxSettings.xhr(); if (myXhr.upload) { myXhr.upload.addEventListener('progress', thisIsAFunction, false); } return myXhr; } }); } });
额外验证小贴士
- 检查浏览器返回的
file.type是否为video/mp4,有些旧浏览器或特殊编码的MP4可能识别错误,此时可以后端直接返回正确的类型给前端。 - 上传完成后,去Google Cloud Console的Storage浏览器里查看文件的内容类型,确认是否已经正确设置为
video/mp4。
内容的提问来源于stack exchange,提问作者tidpe
相关产品推荐
相关产品推荐

