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

如何向Google存储桶预签名URL上传文件?签名报错求助

错误原因分析
  1. x-goog-content-length-range头部误用
    该头部的作用是限制整个上传文件的大小范围(例如0,16383表示仅允许上传0-16383字节的文件),而不是标记分块的位置。你把它当成Content-Range来传递分块起止位置,完全不符合GCS的规范,直接触发了大小校验和签名验证失败。

  2. 请求包含未签名头部
    预签名URL的X-Goog-SignedHeaders明确指定仅对host、x-goog-content-length-range、x-goog-hash这三个头部签名,但你额外添加了Content-Length头部——这个头部不在签名范围内,GCS会直接拒绝请求,返回签名验证错误。

  3. 分块上传方式错误
    GCS的分块(断点续传)上传需要依赖可恢复上传会话,而非对同一个预签名URL重复发起PUT请求。多次PUT同一个预签名URL只会覆盖之前的内容,无法实现分块拼接。

解决方案

方案1:直接上传整个文件(适配当前预签名URL)

API返回的预签名URL大概率是用于单文件上传,而非分块。直接将整个文件内容PUT到该URL即可:

def calculate_crc32c(data: bytes):
    return base64.b64encode(crc32c(data).to_bytes(4, 'big')).decode('utf-8')

def put_file(api: 'API', file: 'File', data: bytes):
    checksum_bs4 = calculate_crc32c(data)
    content_length = len(data)

    with httpx.Client(http2=False) as request:
        response = request.put(
            FILES_URL.format(api.document_storage_uri, file.hash),
            content=data,
            headers=(headers := {
                **api.session.headers,
                'content-length': str(content_length),
                'content-type': 'application/octet-stream',
                'x-goog-hash': f'crc32c={checksum_bs4}',
            })
        )

    if response.status_code == 302:
        print("Full google upload detected, continuing")
        url = response.headers.get("Location")
        print(url)
        with httpx.Client(http2=False) as request:
            # 直接上传整个文件,移除分块逻辑
            response = request.put(
                url,
                content=data,
                headers={
                    'host': 'storage.googleapis.com',
                    'x-goog-hash': f'crc32c={checksum_bs4}',
                }
            )

    if response.status_code != 200:
        raise Exception(f"Put file failed - {response.status_code}\n{response.text}")
    else:
        print(file.uuid, "uploaded")

方案2:使用可恢复分块上传(需API支持)

如果必须分块上传,需要确认API返回的是可恢复上传预签名URL,然后按照GCS规范流程操作:

  1. 初始化会话:向预签名URL发起PUT请求,携带X-Goog-Resumable: start和Content-Length: 0,获取会话URL。
  2. 上传分块:使用会话URL,每个分块请求携带Content-Range: bytes start-end/total头部,以及分块的CRC32C值。

关键注意事项

  • 预签名URL有效期仅59秒,大文件上传需要求API返回更长有效期的URL,或采用可恢复上传。
  • 确保crc32c计算正确:当前代码的大端字节序处理符合GCS要求,无需修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 07:57:26