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

Azure Put Blob API签名文件大小不匹配致认证失败问题求助

嘿,我一眼就看出问题出在哪了——你遇到的签名验证失败,根源是本地计算的文件大小和实际发送给Azure的请求内容长度不匹配!

问题原因拆解

你用os.stat()拿到的19337是本地PDF文件的原始大小,但你在调用requests.put()时用了files参数上传文件。这个参数会自动把请求体封装成multipart/form-data格式,相当于给你的PDF文件套了一层“包装”——里面包含了边界标识、表单字段的头信息等额外内容。这就导致实际发送的HTTP请求Content-Length变成了19497,而你签名时用的是原始文件大小19337,Azure服务端验证签名时会用实际收到的内容长度来计算,两边对不上,自然就认证失败了。

解决办法(两种方案,推荐第一种)

方案1:直接发送原始文件内容(最可靠)

放弃用files参数,直接把文件的原始二进制内容作为请求的data参数发送。这样请求体就是纯PDF内容,Content-Length会和os.stat()获取的大小完全一致,签名验证就能通过。

修改后的代码如下:

import os
import datetime
import base64
import hmac
import hashlib
import requests

storageAccountName = '<storage account name>'
storageContainerName = "<container_name>"
storageKey='<key>'
# 注意路径用r前缀避免转义问题
fd = r"C:\<path>\<to>\<file_to_upload>.pdf"
URI = f'https://{storageAccountName}.blob.core.windows.net/{storageContainerName}/<blob_file_name.pdf>'
version = '2017-07-29'
date = datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")

if os.path.isfile(fd):
    file_info = os.stat(fd)
    file_size = file_info.st_size
    
    # 构造规范化请求字符串
    canonicalized_headers = (
        f"PUT\n\n\n{file_size}\n\napplication/pdf\n\n\n\n\n\n\n"
        f"x-ms-blob-type:BlockBlob\n"
        f"x-ms-date:{date}\n"
        f"x-ms-version:{version}\n"
    )
    canonicalized_resource = f"/{storageAccountName}/{storageContainerName}/<blob_file_name.pdf>"
    canonicalizedString = canonicalized_headers + canonicalized_resource
    
    # 计算签名
    storage_account_key = base64.b64decode(storageKey)
    byte_canonicalizedString = canonicalizedString.encode('utf-8')
    signature = base64.b64encode(hmac.new(
        key=storage_account_key, 
        msg=byte_canonicalizedString, 
        digestmod=hashlib.sha256
    ).digest())
    
    # 构造请求头
    headers = {
        'x-ms-blob-type': "BlockBlob",
        'x-ms-date': date,
        'x-ms-version': version,
        'Authorization': f'SharedKey {storageAccountName}:{signature.decode("utf-8")}',
        'Content-Length': str(file_size),
        'Content-Type': "application/pdf"
    }
    
    # 直接读取文件内容作为data发送,不要用files参数
    with open(fd, 'rb') as file:
        file_content = file.read()
    
    result = requests.put(url=URI, headers=headers, data=file_content)
    print(result.content)

方案2:手动计算multipart请求体的总长度(不推荐)

如果你非要用files参数,就得手动计算整个multipart请求体的长度(包括边界、表单头、文件内容的总和),然后把这个值放到签名的规范化字符串和Content-Length头里。但这种方法很容易出错,因为边界字符串是requests自动生成的,你需要提前捕获它,计算起来非常麻烦,所以强烈建议用方案1。

额外建议

其实更省心的方式是使用Azure官方的Python SDK azure-storage-blob,它会自动处理签名、请求头、内容长度这些细节,完全不用你手动构造请求,能避免很多类似的低级错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:06:51