ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

curl --range 完全指南:用字节区间实现 HTTP/FTP/SFTP/FILE 的部分下载与精准断点续传

curl --range 完全指南:用字节区间实现 HTTP/FTP/SFTP/FILE 的部分下载与精准断点续传 curl --range 完全指南用字节区间实现 HTTP/FTP/SFTP/FILE 的部分下载与精准断点续传【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl本篇技术指南以 curl 命令行的--range短选项-r为核心系统讲解字节区间byte range语法、各协议HTTP/1.1、FTP、SFTP、FILE下的行为差异、与--continue-at的互斥关系并结合当前仓库源码lib/curl_range.c、src/tool_getparam.c、lib/vssh/vssh.c 等剖析其底层实现原理与参数解析细节。读完本文你将能够熟练构造各种区间表达式正确规避多区间响应解析、服务器忽略 Range 请求、上传场景不可靠等陷阱并把同一能力迁移到 libcurl 的CURLOPT_RANGE编程接口上。一、--range是什么--range-r用于只获取指定字节区间内的内容即请求一个部分文档partial document。它是 curl 自 4.0 版本起提供的能力官方文档定义如下Retrieve a byte range (i.e. a partial document) from an HTTP/1.1, FTP or SFTP server or a local FILE.该选项被归类为 http、ftp、sftp、file 四类协议能力见 docs/cmdline-opts/range.md 头部元信息命令行帮助文本为 Retrieve only the bytes within RANGE。其元数据摘要如下属性值长选项--range短选项-r参数range适用协议HTTP、FTP、SFTP、FILE加入版本4.0单次传输可用次数single单次传输只能指定一次与--continue-at的互斥关系--range与断点续传选项--continue-at-C互斥单次传输只能二选一。命令行解析层在 src/tool_getparam.c 直接做了拦截if(config-use_resume) { errorf(--continue-at is mutually exclusive with --range); return PARAM_BAD_USE; }同时在 src/tool_getparam.c 反向检查若已设置--range再使用--continue-at同样报错。--continue-at的语义是从给定字节偏移处继续传输偏移量表示从源文件开头跳过的确切字节数而--range表达的是要哪一段。二者的底层其实共享同一套实现——见下文Curl_range()的分析。--continue-at还额外规定不能与--no-clobber、--remove-on-error同用见 docs/cmdline-opts/continue-at.md。二、区间语法速查--range支持多种写法下表汇总了全部标准形式均继承自 docs/cmdline-opts/range.md语法含义示例0-499前 500 字节第 0499 字节500-999紧接着的 500 字节第 500999 字节-500最后 500 字节文件末尾的 500 字节9500-从偏移 9500 开始直到文件末尾第 9500 字节及之后全部内容0-0,-1只取第一个字节和最后一个字节仅 HTTP两个互不相连的区间100-199,500-599两个独立的 100 字节区间仅 HTTP逗号分隔多区间注意start与stop字段只允许数字字符0-9。若在start-stop区间语法中混入非数字字符服务器返回的内容将是未定义行为取决于服务器自身的配置。这一点在命令行解析阶段就有主动校验详见下文第三节。三、命令行的解析与容错细节必须有连字符在 src/tool_getparam.c 中curl 对纯数字、不带连字符的区间做了特殊兼容处理——因为不带短横线的区间严格来说不构成合法 HTTP Rangeman 手册早期曾误称其为合法写法if(!curlx_str_number(nextarg, value, CURL_OFF_T_MAX) curlx_str_single(nextarg, -)) { /* Specifying a range WITHOUT A DASH does create an illegal HTTP range ... */ char buffer[32]; warnf(A specified range MUST include at least one dash (-). Appending one for you); curl_msnprintf(buffer, sizeof(buffer), % CURL_FORMAT_CURL_OFF_T -, value); ... }也就是说执行curl --range 100 $URL会得到警告 A specified range MUST include at least one dash (-). Appending one for you然后被自动改写成100-从第 100 字节到文件末尾。非法字符告警对于非纯数字参数解析器会逐字符扫描src/tool_getparam.c一旦发现既不是数字也不是-、,的字符即发出警告服务器对该请求的响应是不确定的while(*nextarg) { if(!ISDIGIT(*nextarg) *nextarg ! - *nextarg ! ,) { warnf(Invalid character is found in given range. A specified range MUST have only digits in \start\-\stop\. The servers response to this request is uncertain.); break; } nextarg; }四、底层实现Curl_range()如何把区间翻译成下载参数命令行解析只是把字符串存进config-range真正决定下载多少字节、从哪里开始的是 lib 层的Curl_range()函数定义于 lib/curl_range.c声明见 lib/curl_range.h。该函数仅在启用 FTP 或 FILE 之一时参与编译#if !defined(CURL_DISABLE_FTP) || !defined(CURL_DISABLE_FILE)。它使用curlx_str_number()、curlx_str_single()这两个字符串解析辅助函数来自 lib/curlx/strparse把区间字符串拆解为三个分支X-型只有起点data-state.resume_from from;语义为从 X 读到文件末尾。-Y型只有终点data-req.maxdownload to;>s-range curl_maprintf(% FMT_OFF_T -, s-resume_from); /* resume 情形 */ ... s-use_range TRUE; /* enable range download */五、各协议的行为差异HTTP多区间与服务器可能忽略单区间curl 在 GET/HEAD 请求中追加Range: bytesX-Y头见 lib/http.c 的http_range()函数。若用户自定义了Range头则用户头优先内部生成的被跳过。多区间逗号分隔的多区间仅 HTTP 支持。若服务器支持会以标准 MIME 分段技术返回multipart 响应curl 原样透传as-is给调用方。该响应除了请求的字节外还包含元信息如Content-Type: multipart/byteranges及各段的 boundary、Content-Range头解析或转换这些内容完全是调用方的责任。这一点在 docs/libcurl/opts/CURLOPT_RANGE.md 中也有完全一致的说明。服务器可能忽略RFC 7233 第 3.1 节允许服务器无视 Range 请求因此很多 HTTP/1.1 服务器并未启用该特性——当 curl 尝试获取区间时可能拿到的是整个文档。这属于协议允许的正常行为客户端无法强制。FTP / SFTP仅支持简单start-stop语法FTP 与 SFTP 的区间下载只支持简单的start-stop语法可省略其中一个数字不支持逗号多区间。FTP 依赖扩展命令 SIZElib/ftp.c 中通过REST offset命令实现偏移定位并在FTP_REST/FTP_RETR_REST状态机中处理lib/ftp.c、lib/ftp.c。同时 curl 需要先通过 SIZE 命令获知文件大小才能处理-Y末尾 N 字节这类依赖文件总长的区间若服务器不支持 SIZE行为会受限。SFTPlibssh/libssh2 两个后端在下载前都会调用Curl_ssh_range()来计算实际起止偏移见 lib/vssh/libssh.c、lib/vssh/libssh2.c。该函数的实现位于 lib/vssh/vssh.cCURLcode Curl_ssh_range(struct Curl_easy *data, const char *range, curl_off_t filesize, curl_off_t *startp, curl_off_t *sizep)关键逻辑包括起点缺省时-Y型以文件大小推算from filesize - to; to filesize - 1;且-0非法起点超出文件大小时报错 Offset (...) was beyond file size (...)终点缺省或超出文件大小时截断到filesize - 1from to时报错 Bad range: start offset larger than end offset。这些行为在 tests/unit/unit2605.c 中有一组专门的单元测试用例覆盖该文件构造了多种struct range场景调用Curl_ssh_range验证 start/size 计算结果。FILE本地文件区间本地file://协议同样支持区间读取。对 FILE 的区间支持自 libcurl 7.18.0 起可用见 docs/libcurl/opts/CURLOPT_RANGE.md。由于本地文件大小可以直接获取区间计算最为可靠不存在服务器忽略请求的问题。六、典型使用示例# 取前 500 字节HTTP/FTP/SFTP/FILE 均可 curl --range 0-499 https://example.com/file.bin # 取第二段 500 字节 curl -r 500-999 https://example.com/file.bin # 取最后 500 字节依赖服务器支持 SIZE / 可知文件大小 curl -r -500 https://example.com/file.bin # 从第 9500 字节读到文件末尾 curl -r 9500- https://example.com/file.bin # 只取首尾各一个字节仅 HTTPmultipart 响应需自行解析 curl -r 0-0,-1 https://example.com/file.bin # 取两个独立的 100 字节区间仅 HTTP curl -r 100-199,500-599 https://example.com/file.bin # 对本地文件取区间 curl -r 100-199 file:///path/to/local/file.bin对应上述示例仓库测试集里有现成的验证用例可参考tests/data/test1032 ——HTTP HEAD with --range命令为--range 1-3 --headtests/data/test336、tests/data/test337 —— FTP 下--range 3-6的区间下载测试。七、HTTP 上传POST/PUT场景的注意事项当--range用于 HTTP 上传POST 或 PUT时功能不被保证。HTTP 协议没有标准、可互操作的断点续传上传机制curl 为此使用的一组自定义头曾在某些服务器上验证可用之后便保留给认为有用的用户使用。源码层面lib/http.c 的http_range()对 POST/PUT 走的是Content-Range头分支且 resume 为负值set_resume_from 0时按上传整个文件处理。因此生产环境中不要把区间上传当作可靠特性若确有类似需求优先使用专门的续传/分片方案或自行实现 Content-Range 逻辑并做好服务器兼容性测试。八、编程接口libcurl 的CURLOPT_RANGE同样的能力可通过 libcurl API 使用选项为CURLOPT_RANGE加入于 libcurl 7.1见 docs/libcurl/opts/CURLOPT_RANGE.md#include curl/curl.h int main(void) { CURL *curl curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, https://example.com); /* 取前 200 字节 */ curl_easy_setopt(curl, CURLOPT_RANGE, 0-199); result curl_easy_perform(curl); curl_easy_cleanup(curl); } return 0; }要点参数为char *格式与命令行一致X-YX 或 Y 可省略HTTP 支持X-Y,N-M多区间服务器返回 multipart 响应libcurl 原样返回、由调用方自行解析服务器可能忽略区间请求RFC 7233 §3.1因此即使设置了CURLOPT_RANGE也可能收到完整响应RTSP 例外RTSP 的区间遵循 RFC 2326 §12.29不允许字节区间只能用npt、utc、smpte格式RTSP 支持自 7.20.0 起重复设置以最后一次为准传入NULL可禁用应用无需在设置后保留字符串libcurl 内部会拷贝默认值为NULL不启用区间。在 lib/setopt.c 中可以看到CURLOPT_RANGE直接经由Curl_setstropt(data, STRING_SET_RANGE, ptr)存入内部字符串选项随后在请求构造时被 lib/url.c 引用并激活use_range标志。九、常见问题与排查思路Q1明明指定了区间却下载了整个文件HTTP 服务器被 RFC 7233 允许忽略 Range 请求检查服务器是否真的处理了Range头可用curl -v观察响应头若返回 206 Partial Content 表示生效返回 200 表示被忽略。FTP 场景则确认服务器是否支持 SIZE/REST 扩展命令。Q2多区间响应看不懂-r 0-0,-1之类的多区间请求会得到multipart/byteranges响应curl 不会解析它需要自行按 MIME boundary 切分并提取各段的Content-Range元信息。Q3--range和--continue-at能一起用吗不能。命令行解析层会在 src/tool_getparam.c 直接报错退出单次传输二者互斥。Q4区间里写了字母会怎样命令行会发出 Invalid character is found in given range 警告且服务器响应未定义请保证start/stop只含 0-9 数字。Q5想要断点续传自动定位偏移用--continue-at -即-C -curl 会根据目标文件已存在的大小自动计算续传起点这是与--range定位截然不同的使用场景详见 docs/cmdline-opts/continue-at.md。十、小结--range把取文件某一段这件事抽象成了一套简洁的区间语法X-Y、X-、-Y覆盖了绝大多数定位需求逗号多区间是 HTTP 独有能力。其实现横跨命令行解析src/tool_getparam.c、区间翻译lib/curl_range.c、HTTP 头构造lib/http.c、FTP REST 命令lib/ftp.c与 SFTP 偏移计算lib/vssh/vssh.c多个层次最终统一收敛到resume_frommaxdownload这两个内部字段。使用时要牢记三件事HTTP 服务器可能忽略区间请求多区间响应需要自行解析上传场景功能不受保证。掌握这些边界你就能在下载大文件片段、日志抽样、多媒体分段拉取等场景中精准控制传输内容。【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进