You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

curl上传GitHub Release大文件可正常获取响应,Python requests上传成功后却超时/挂起的原因

curl上传GitHub Release大文件可正常获取响应,Python requests上传成功后却超时/挂起的原因

我遇到过类似的问题,结合你提供的curl和requests的对比信息,主要原因和解决思路可以分成这几点:

核心原因分析

1. HTTP协议版本的差异处理

看你的curl日志,它自动协商使用了HTTP/2(日志里有ALPN: server accepted h2),而标准的requests库基于urllib3,默认是用HTTP/1.1的。GitHub的uploads服务器在处理大文件(100MB+)时,HTTP/1.1下的响应返回逻辑可能存在延迟——比如服务器需要额外时间完成文件校验、同步到CDN,这时候HTTP/1.1的连接复用机制可能导致requests卡在等待响应的状态;而HTTP/2的多路复用和流处理机制更高效,能顺利拿到服务器返回的201响应。

2. 超时设置的问题

你在requests里设置的timeout=2或者timeout=20总超时时间,包含了上传时间和等待响应的时间。大文件上传本身就需要不少时间,留给服务器返回响应的时间就被压缩了,很容易触发读取超时。而curl默认没有设置超时,会一直等待直到收到响应,所以不会出现这个问题。

3. 请求头的细微差异

你的curl请求里带了X-GitHub-Api-Version: 2022-11-28,并且Accept头是application/vnd.github+json,但requests里用的是application/vnd.github.v3+json(虽然这俩语义相近),缺少API版本头。这种细微差异可能导致服务器处理响应的逻辑不同,间接引发超时。


对应的解决方案

方案1:让requests支持HTTP/2

给requests添加HTTP/2支持,和curl保持一致的协议版本:
首先安装依赖:

pip install requests[http2]

然后修改代码,启用HTTP/2:

import os
import requests
from urllib3.contrib.pyopenssl import inject_into_urllib3

# 注入HTTP/2支持到urllib3
inject_into_urllib3()

release_id = "93670877"
file = "spa_eng.apkg"
url = f"https://uploads.github.com/repos/Vuizur/tatoeba-to-anki/releases/{release_id}/assets?name={file}"

headers = {
    "Accept": "application/vnd.github+json",
    "Content-Type": "application/octet-stream",
    "Authorization": f"Bearer {os.getenv('GITHUB_TOKEN')}",
    "X-GitHub-Api-Version": "2022-11-28"  # 和curl保持一致
}

with open(file, "rb") as f:
    # 分开设置连接超时和读取超时,给服务器足够的响应时间
    response = requests.post(url, headers=headers, data=f, timeout=(10, 60))
print(response.status_code)
print(response.json())

方案2:改用原生支持HTTP/2的httpx库

如果觉得requests加HTTP/2的配置麻烦,可以直接用httpx,它原生支持HTTP/2,用法和requests很像:

pip install httpx

代码示例:

import os
import httpx

release_id = "93670877"
file = "spa_eng.apkg"
url = f"https://uploads.github.com/repos/Vuizur/tatoeba-to-anki/releases/{release_id}/assets?name={file}"

headers = {
    "Accept": "application/vnd.github+json",
    "Content-Type": "application/octet-stream",
    "Authorization": f"Bearer {os.getenv('GITHUB_TOKEN')}",
    "X-GitHub-Api-Version": "2022-11-28"
}

with open(file, "rb") as f:
    with httpx.Client(http2=True) as client:
        # 同样分开设置超时,读取超时设长一点
        response = client.post(url, headers=headers, content=f, timeout=(10, 60))
print(response.status_code)
print(response.json())

方案3:优化requests的超时设置(不换协议)

如果不想改协议,就把超时设置成分开的连接超时和读取超时,给服务器足够的时间返回响应:

import os
import requests

release_id = "93670877"
file = "spa_eng.apkg"
url = f"https://uploads.github.com/repos/Vuizur/tatoeba-to-anki/releases/{release_id}/assets?name={file}"

headers = {
    "Accept": "application/vnd.github+json",
    "Content-Type": "application/octet-stream",
    "Authorization": f"Bearer {os.getenv('GITHUB_TOKEN')}",
    "X-GitHub-Api-Version": "2022-11-28"
}

with open(file, "rb") as f:
    # (连接超时10秒,读取超时120秒),根据你的文件大小调整读取超时
    response = requests.post(url, headers=headers, data=f, timeout=(10, 120))
print(response.status_code)
print(response.json())

备注:内容来源于stack exchange,提问作者Pux

火山引擎 最新活动