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

使用PHP 7库生成签名URL向Google Storage上传大文件时文件损坏问题求助

解决Google Storage大文件上传后损坏及Content-Type错误的问题

我来帮你排查下这个问题,你遇到的文件损坏和类型错误其实是同一个核心问题导致的,咱们一步步拆解解决:

问题根源

你当前的前端上传逻辑犯了两个关键错误:

  1. 用FormData包装文件上传:PUT请求直接传FormData会让请求体变成multipart/form-data格式(包含字段名、分隔符等额外内容),而不是原始的MP4二进制数据。Google Storage存下来的是这个FormData的内容,自然无法正常播放。
  2. 没正确处理可恢复上传流程:后端用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 04:37:30