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

为何Python Requests中PUT请求超时而GET请求不超时?

Requests中PUT与GET超时行为不一致的原因及解决方法

问题核心原因

先明确Requests超时参数(connect, read)的实际作用:

  • connect:仅限制TCP连接建立阶段的耗时,正常建立连接后这个参数就失效了
  • read:限制客户端等待服务器响应的间隔时间——也就是客户端发送完请求后,多久没收到服务器的回复就触发超时

你的PUT请求出现异常的根源在于:你把整个大文件读入内存后用data=dat一次性发送,此时Requests会把整个请求体作为一个连续的数据流发送。在低速网络下,这个发送过程会持续很久,但此时客户端还没完成请求发送,read超时(等待服务器响应)根本还没开始生效;而底层实现中,会错误地把整个请求发送时长和connect超时绑定,导致connect超时设得比总发送时长小就直接失败。

而GET请求的场景是:客户端发送完请求头后就开始接收服务器的响应数据,read超时监控的是两次接收数据包之间的间隔,只要每个间隔不超过设置的时长,哪怕总耗时很久也不会触发超时,这是符合设计预期的。

解决方案

1. 直接传入文件对象分块发送(推荐)

不要一次性把文件读入内存,直接传入文件对象让Requests自动分块发送:

with open(PATH_TO_1GB_FILE, "rb") as f:
    timeouts = (10, 1)  # connect=10秒(足够建立连接),read=1秒(监控数据传输间隔)
    start = datetime.now()
    requests.put(
        f"{local}/puttest",
        headers={"User-Agent": "Mozilla/5.0"},
        timeout=timeouts,
        data=f,  # 直接传文件对象,而非读入内存的bytes
        verify=cert
    )
    print(f"PUT: {datetime.now()-start}")

这样Requests会分块读取文件并发送,每块传输后会检查间隔超时:如果网络断开,超过1秒没数据传输就会触发超时;而正常低速传输时,只要每块之间的间隔不超过1秒,总时长再久也能完成上传。

2. 手动控制分块上传逻辑

如果需要更精细的控制,可以手动分块读取文件并发送,比如每次发送1MB:

CHUNK_SIZE = 1024 * 1024  # 1MB
with open(PATH_TO_1GB_FILE, "rb") as f:
    timeouts = (10, 1)
    start = datetime.now()
    # 初始化流式请求
    with requests.put(
        f"{local}/puttest",
        headers={"User-Agent": "Mozilla/5.0", "Transfer-Encoding": "chunked"},
        timeout=timeouts,
        verify=cert,
        stream=True
    ) as req:
        while chunk := f.read(CHUNK_SIZE):
            req.raw.write(chunk)
            req.raw.flush()
    print(f"PUT: {datetime.now()-start}")

这种方式可以完全控制分块大小,同时利用stream=True让超时逻辑监控每个分块的传输间隔。

3. 调整urllib3的超时配置

如果不想改代码结构,可以通过HTTPAdapter配置urllib3的socket_timeout,这个参数会监控整个连接的任意阶段的无数据间隔,包括发送请求体的过程:

from requests.adapters import HTTPAdapter
from urllib3.util.timeout import Timeout

adapter = HTTPAdapter(max_retries=0)
# total=None关闭总时长限制,只监控连接和传输间隔超时
adapter.timeout = Timeout(connect=10, read=1, total=None)

session = requests.Session()
session.mount("https://", adapter)
session.mount("http://", adapter)

with open(PATH_TO_1GB_FILE, "rb") as f:
    dat = f.read()
    start = datetime.now()
    session.put(
        f"{local}/puttest",
        headers={"User-Agent": "Mozilla/5.0"},
        data=dat,
        verify=cert
    )
    print(f"PUT: {datetime.now()-start}")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 04:42:22