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

Python requests下载文件遇ChunkedEncodingError,如何实现断点续传?

流式下载断点续传解决方案

针对Heroku服务器连接不稳定导致的下载中断问题,核心解决思路是利用HTTP的Range请求头实现断点续传——每次断连后,从已经下载的字节位置继续请求剩余数据,而非从头开始重新下载。

实现步骤

  • 通过HEAD请求获取目标文件总大小,避免一开始就加载大文件
  • 检查本地已下载的文件大小,构造Range请求头告知服务器从指定字节位置开始传输
  • 捕获连接异常并设置重试机制
  • 以追加模式打开文件,写入后续下载的数据块

完整代码示例

import requests
import os

def download_file(url, headers, filename, max_retries=5):
    save_path = f"./files/{filename}"
    total_size = 0
    downloaded_size = 0

    # 获取文件总大小
    try:
        head_res = requests.head(url, headers=headers)
        head_res.raise_for_status()
        total_size = int(head_res.headers.get('Content-Length', 0))
        print(f"文件总大小: {round(total_size/(1024*1024),3)}MB")
    except Exception as e:
        print(f"获取文件信息失败: {str(e)}")
        return

    # 读取本地已下载的字节数
    if os.path.exists(save_path):
        downloaded_size = os.path.getsize(save_path)
        print(f"已下载: {round(downloaded_size/(1024*1024),3)}MB,继续下载剩余部分...")

    # 已下载完成则直接返回
    if downloaded_size >= total_size and total_size != 0:
        print("文件已下载完成")
        return

    retries = 0
    while retries < max_retries:
        try:
            # 构造断点续传的Range请求头
            range_headers = headers.copy()
            range_headers['Range'] = f'bytes={downloaded_size}-'
            
            with requests.get(url, headers=range_headers, stream=True, timeout=30) as res:
                res.raise_for_status()
                # 验证服务器是否支持断点续传
                if res.status_code != 206 and downloaded_size > 0:
                    print("服务器不支持断点续传,将从头开始下载")
                    downloaded_size = 0
                    os.remove(save_path)
                    continue

                with open(save_path, 'ab') as file:
                    for chunk in res.iter_content(chunk_size=8192):
                        if chunk:
                            file.write(chunk)
                            downloaded_size += len(chunk)
                            print(f"已下载: {round(downloaded_size/(1024*1024),3)}MB / {round(total_size/(1024*1024),3)}MB", end="\r")
            
            # 验证下载完整性
            if os.path.getsize(save_path) == total_size or total_size == 0:
                print("\n下载完成")
                return
            else:
                print(f"\n下载未完成,已下载{downloaded_size}字节,目标{total_size}字节,重试中...")
                retries += 1

        except requests.exceptions.RequestException as e:
            print(f"\n连接中断: {str(e)},重试中...")
            retries += 1

    print(f"已重试{max_retries}次仍失败,请检查网络或服务器状态")

# 使用示例
# download_file("你的目标URL", {"自定义请求头"}, "目标文件名.mp4")

关键细节说明

  1. Range请求头:格式为bytes=start-,服务器会返回从start字节到文件末尾的内容,响应状态码为206(Partial Content)
  2. 追加写入模式:用'ab'模式打开文件,确保已下载的内容不会被覆盖
  3. 重试机制:设置最大重试次数,避免无限循环等待
  4. 完整性验证:下载结束后对比本地文件大小与服务器返回的总大小,确保文件完整
  5. 兼容性处理:如果服务器不支持Range请求,自动切换为从头下载

注意事项

  • 确认目标服务器支持Range请求(Heroku托管的静态文件或多数API均支持)
  • 若服务器响应无Content-Length(如动态生成内容),可移除总大小验证逻辑,持续下载至连接正常结束
  • 可根据网络状况调整chunk_size和timeout参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 03:24:57