如何在 Python 3.5 中恢复文件下载?

ibr*_*tin 2 python python-3.x python-requests python-3.5 python-3.6

我正在使用 python 3.5 requests 模块使用以下代码下载文件,如何使此代码“自动恢复”从部分下载的文件中下载。

response = requests.get(url, stream=True)

total_size = int(response.headers.get('content-length'))  

with open(file_path + file_name, "wb") as file:
    for data in tqdm(iterable = response.iter_content(chunk_size = 1024), total = total_size//1024, unit = 'KB'):
        file.write(data)
Run Code Online (Sandbox Code Playgroud)

requests如果可能的话,我宁愿只使用模块来实现这一点。

aba*_*ert 9

我不认为requests这是内置的——但你可以很容易地手动完成(只要服务器支持它)。

关键是范围请求。要获取从字节 12345 开始的部分资源,请添加以下标头:

Range: bytes=12345-
Run Code Online (Sandbox Code Playgroud)

然后您可以将结果附加到您的文件中。


理想情况下,您应该验证您返回的是 a206 Partial Content而不是 a 200,并且标题包括您想要的范围:

Content-Range: bytes 12345-123456/123456
Content-Length: 111112
Run Code Online (Sandbox Code Playgroud)

您可能还想预先验证服务器处理范围。您可以通过查看初始响应中的标头来执行此操作,或者通过执行 aHEAD来检查此内容:

Accept-Ranges: bytes
Run Code Online (Sandbox Code Playgroud)

如果标头完全丢失,或者none作为一个值,或者有一个不包含的值列表bytes,则服务器不支持恢复。

并且还可以检查Content-Length以确认您在被中断之前没有完成整个文件。


所以,代码看起来像这样:

def fetch_or_resume(url, filename):
    with open(filename, 'ab') as f:
        headers = {}
        pos = f.tell()
        if pos:
            headers['Range'] = f'bytes={pos}-'
        response = requests.get(url, headers=headers, stream=True)
        if pos:
            validate_as_paranoid_as_you_want_to_be_(pos, response)
        total_size = int(response.headers.get('content-length'))  
        for data in tqdm(iterable = response.iter_content(chunk_size = 1024), total = total_size//1024, unit = 'KB'):
            file.write(data)
Run Code Online (Sandbox Code Playgroud)

编写下载管理器类型软件的人的一个常见错误是试图跟踪先前请求中读取了多少。不要这样做,只是使用文件本身来告诉您您拥有多少。毕竟,如果您读取 23456 字节但只将 12345 刷新到文件中,那么 12345 就是您想要开始的地方。