content-range响应头仅在206状态码下合法存在,需先校验状态码和字段存在性,再用std::from_chars安全解析bytes 123-456/789中的三段数字,严格处理*/total、416错误及写入偏移边界。

Content-Range响应头不是总存在,先确认状态码和头部是否合法
收到 HTTP 响应后,不能直接假设 Content-Range 一定存在。它只在服务器返回部分响应(如带 Range 请求头的 GET)时出现,且仅当状态码为 206 Partial Content 时才语义正确;若返回 200 OK 却带 Content-Range,说明服务端行为异常,应降级为全量下载并清空已有文件。
解析前必须检查:
- 响应状态码是否为
206(200表示全量,416表示范围无效) -
Content-Range字段是否存在于响应头中(用小写比对更安全,如"content-range") - 字段值非空,且不为
"bytes */12345"这类未知起始位置的格式(表示不可续传)
用 std::string_view 安全提取 bytes 123-456/789 中的三段数字
Content-Range 的标准格式是 bytes <start>-<end>/<total></total></end></start>,但实际响应可能含空格、大小写混用、或缺失 /total(如 bytes 1024-2047/*)。别用 std::strtok 或连续 find(':') 拆分——它无法处理字段值内嵌空格或非法格式。
推荐做法是:先跳过 "bytes " 前缀,再按 '-' 和 '/' 分割,每段用 std::from_chars 转换:
std::string_view cr = get_header_value("content-range");
if (cr.substr(0, 6) != "bytes ") return std::nullopt;
cr = cr.substr(6); // 跳过 "bytes "
<p>size_t dash = cr.find('-');
if (dash == std::string_view::npos) return std::nullopt;</p><p>size_t slash = cr.find('/', dash);
if (slash == std::string_view::npos) return std::nullopt;</p><p>auto [ptr1, ec1] = std::from_chars(cr.data(), cr.data() + dash, start);
if (ec1 != std::errc{} || ptr1 != cr.data() + dash) return std::nullopt;</p><p>auto [ptr2, ec2] = std::from_chars(cr.data() + dash + 1, cr.data() + slash, end);
if (ec2 != std::errc{} || ptr2 != cr.data() + slash) return std::nullopt;</p><p>// total 可选:若 slash 后为空或 "<em>",则 total = std::nullopt
std::string_view total_sv = cr.substr(slash + 1);
if (!total_sv.empty() && total_sv != "</em>") {
auto [ptr3, ec3] = std::from_chars(total_sv.data(), total_sv.data() + total_sv.size(), total);
if (ec3 != std::errc{}) return std::nullopt;
}</p>
遇到 bytes */12345 或 416 错误时别硬解析
服务端返回 Content-Range: bytes */12345 表示“你请求的范围无效,但我愿意告诉你文件总长”,此时 start 和 end 不可信任,不能用于续传定位。同理,若请求返回 416 Requested Range Not Satisfiable,说明本地记录的偏移已失效,应删除临时文件、重发 HEAD 获取 Accept-Ranges 和 Content-Length,再决定是否重试。
常见错误包括:
- 把
*/12345中的12345当作end去 seek 写入 —— 实际应视作新文件总大小 - 收到
416后仍尝试 parseContent-Range—— 此时该字段可能根本不存在或格式错乱 - 用
std::stoi解析end导致溢出或抛异常,而没做 try/catch ——std::from_chars是唯一推荐方式
写入文件时注意偏移与长度校验必须同步
拿到 start、end 后,要确保接收的 body 字节数等于 end - start + 1,否则数据损坏。不要只信 header,得在读取响应体时计数验证。
写入时务必用 r+b 模式打开文件,并 fseek(fp, start, SEEK_SET) 定位;若使用 std::ofstream,需调用 seekp(start) 并确认返回值有效。更关键的是:每次写完一块,立即 fsync() 或至少 fflush(),避免断电或崩溃导致偏移状态丢失。
容易被忽略的一点:Content-Range 中的 end 是包含的(即 bytes 1024-2047 共 1024 字节),但很多初学者误以为是长度,结果多写一个字节或少写一个字节,导致文件错位。这个边界含义必须和实际 write 长度严格对齐。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











