flask需手动解析range头:用request.headers.get("range")提取字符串,正则匹配bytes=start-end格式,支持bytes=start-(至末尾),校验合法性后返回206响应,设置content-range、accept-ranges和content-length,并用二进制文件句柄seek+read分块读取。

Flask如何正确解析Range请求头
客户端发起断点续传时,会带 Range: bytes=1024-2047 这样的请求头,Flask 默认不自动解析它——request.headers.get('Range') 返回的是原始字符串,需要手动提取起始和结束字节。直接用 request.range 会报 AttributeError,因为 Flask 的 Request 类默认不启用 range 解析(不像某些 WSGI 中间件或 FastAPI 那样内置支持)。
实操建议:
- 用正则提取
bytes=(\d+)-(\d+)?,注意结尾可能为空(如bytes=1024-表示“从 1024 到末尾”) - 必须校验数值合法性:起始不能为负、结束不能小于起始、不能超出文件总大小
- 若请求头格式错误或缺失,应返回
200 OK全量响应,而非416 Range Not Satisfiable——这是 HTTP/1.1 规范要求
如何构造符合规范的206响应头与分块体
返回 206 Partial Content 不只是改状态码,响应头必须严格匹配 RFC 7233:包括 Content-Range、Accept-Ranges: bytes、Content-Length(是本次返回的字节数,不是整个文件),且 Content-Type 必须与文件实际 MIME 类型一致(如 video/mp4)。
常见错误现象:
- 漏设
Accept-Ranges→ iOS Safari 拒绝断点续传 -
Content-Range写成bytes 0-1023/1024(缺等号)→ Chrome 报ERR_CONTENT_LENGTH_MISMATCH - 用
send_file()直接传 file-like 对象但没指定as_attachment=False和download_name=None→ 触发强制下载,破坏流式播放
推荐做法:用 Response 手动构造流式响应,配合 yield 分块读取文件,避免内存加载整段视频。
如何安全读取大视频文件并控制内存占用
视频文件动辄几百 MB,用 open().read() 或 send_file(path, ...) 在无参数控制时会把整个文件读进内存,导致 OOM。必须按需读取字节范围,并限制单次读取大小(如 8KB)。
实操要点:
- 用
open(path, 'rb')获取文件句柄,seek(start_byte)定位,再循环read(chunk_size) - 设置合理
chunk_size(建议 4096–65536):太小增加系统调用开销,太大吃内存 - 务必在
try/finally或with中关闭文件句柄,否则高并发下 fd 耗尽 - 若视频存于 NFS 或对象存储(如 S3),不能直接
seek,需换用支持 range 的 client(如boto3的get_object(Range=...))
为什么Nginx反代下Range请求常失效
本地调试时一切正常,部署到生产环境后断点下载失效,大概率是 Nginx 默认透传 Range 头但不透传 Content-Range 和 Accept-Ranges,或者开启了 proxy_buffering on 导致响应被缓存并丢弃部分 header。
关键配置项(加在 location 块内):
-
proxy_pass_request_headers on;(确保 Range 头到达 Flask) -
proxy_set_header Accept-Ranges bytes;(强制透传) -
proxy_hide_header Content-Range;→ 错!应删掉这行,否则 Nginx 会屏蔽它 -
proxy_buffering off;(避免缓冲破坏流式响应) -
chunked_transfer_encoding on;(配合 Flask 流式 yield)
最易被忽略的是:Nginx 默认对 206 响应不设置 Content-Range,必须由后端(Flask)完整输出,Nginx 不会帮你补。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











