bigywater.tech
Flask · 49

Range 请求与断点下载

大文件下载中断了,怎么接着下?服务器怎么回应 Range?哪些写法会得到 416?流式响应能不能支持断点续传?这篇用真实的 curl 续传和 13 种 Range 写法验证。

206
Range 合法时的状态码
416
越界、反向、多段、非 bytes 单位的 Range 得到的状态码
7240
续传时 curl 实际传输的字节数(总共 10240,已有 3000)

Range 与 206,不合法的 Range

实测 · 本机执行输出
  无 Range: 200 | Accept-Ranges = bytes | Content-Length = 10240
  Range: bytes=0-99           → 206 | Content-Range: bytes 0-99/10240 | 收到   100 字节 | 内容与文件对应片段一致:True
  Range: bytes=100-199        → 206 | Content-Range: bytes 100-199/10240 | 收到   100 字节 | 内容与文件对应片段一致:True
  Range: bytes=-50            → 206 | Content-Range: bytes 10190-10239/10240 | 收到    50 字节
  Range: bytes=10000-         → 206 | Content-Range: bytes 10000-10239/10240 | 收到   240 字节
  Range: bytes=0-0            → 206 | Content-Range: bytes 0-0/10240 | 收到     1 字节 | 内容与文件对应片段一致:True
  Range: bytes=10239-10239    → 206 | Content-Range: bytes 10239-10239/10240 | 收到     1 字节 | 内容与文件对应片段一致:True
  Range: bytes=0-999999       → 206 | Content-Range: bytes 0-10239/10240 | 收到 10240 字节 | 内容与文件对应片段一致:True
  Range: bytes=10240-         → 416 | Content-Range: bytes */10240 | 收到   177 字节
  Range: bytes=20000-30000    → 416 | Content-Range: bytes */10240 | 收到   177 字节
  Range: bytes=5-2            → 416 | Content-Range: bytes */10240 | 收到   177 字节
  Range: bytes=0-9,20-29      → 416 | Content-Range: bytes */10240 | 收到   177 字节
  Range: items=0-5            → 416 | Content-Range: bytes */10240 | 收到   177 字节
  Range: bytes=abc            → 416 | Content-Range: bytes */10240 | 收到   177 字节

上一篇:Session Cookie 的数据结构;下一篇:Content Negotiation;send_file 的更多细节:send_file() 与 send_from_directory()。

If-Range

实测 · 本机执行输出
  Range: bytes=0-9 + If-Range = 当前 ETag           → 206 收到 10 字节
  Range: bytes=0-9 + If-Range = 过期的 ETag          → 200 收到 10240 字节
  Range: bytes=0-9 + If-Range = 当前 Last-Modified  → 206 收到 10 字节

断点续传

实测 · 本机执行输出
  第一次(只取 0-2999)文件大小: 3000
  curl -C - 续传: 206 本次传输 7240 字节 | 最终文件大小 10240
  SHA-256 与原文件相同: True

生成器与自己实现

实测 · 本机执行输出
  /gen  Range: bytes=0-99 → 200 | Accept-Ranges = None | 收到 1000 字节
  /custom Range: bytes=100-199 → 206 | Content-Range = bytes 100-199/10240 | 收到 100 字节 | 内容正确: True
  /custom Range: bytes=99999- → 416 | Content-Range = bytes */10240

HEAD

实测 · 本机执行输出
  HEAD + Range: bytes=0-9 → 206 | Content-Range = bytes 0-9/10240 | Content-Length = 10 | 正文 0

分步演示

Range 请求
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动
    • 没有验证:多段 Range(multipart/byteranges)的完整实现、Nginx 或 CDN 对 Range 的缓存行为、浏览器视频播放时发出的 Range 序列、大文件的并行分段下载
    京ICP备2026067502号-1京公网安备11010502063487号