news 2026/9/11 16:51:39

使用 CURLOPT_WILDCARDMATCH 实现 libcurl 的 FTP 目录通配符批量下载

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 CURLOPT_WILDCARDMATCH 实现 libcurl 的 FTP 目录通配符批量下载

使用 CURLOPT_WILDCARDMATCH 实现 libcurl 的 FTP 目录通配符批量下载

【免费下载链接】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

CURLOPT_WILDCARDMATCH是 libcurl 提供的目录通配符传输开关:开启后,URL 末尾的文件名部分可以使用类似 shell 的fnmatch通配符模式(如ftp://example.com/some/path/*.txt),libcurl 会先列出远程目录,再自动批量下载所有匹配的文件。本指南以 curl 仓库中的 CURLOPT_WILDCARDMATCH 手册 为主体,结合 lib/ftp.c、lib/curl_fnmatch.c 与 include/curl/curl.h 的源码实现,完整讲解模式语法、回调协作机制与底层状态机,帮助读者用 libcurl 实现类似 FTP 批量镜像、日志收集、按日期规则取文件等实战需求。

选项总览

#include <curl/curl.h> CURLcode curl_easy_setopt(CURL *handle, CURLOPT_WILDCARDMATCH, long onoff);
  • 作用:开启/关闭目录通配符传输。将onoff设为1即按文件名模式传输多个文件,设为0(默认值)则关闭。
  • 适用协议:仅 FTP(该选项在#ifndef CURL_DISABLE_FTP保护块内定义与生效)。
  • 加入版本:7.21.0。
  • 模式来源:通配符模式作为CURLOPT_URL中 URL 末尾的"文件名"部分给出,即最后一个/之后的内容被视为模式。

从源码看,该选项最终落到struct Curl_easyset结构体中:在 lib/setopt.c 中,case CURLOPT_WILDCARDMATCH:将参数直接写入s->wildcard_enabled,对应 lib/urldata.h 中的BIT(wildcard_enabled); /* enable wildcard matching */字段。同时 lib/easyoptions.c 将该选项登记为CURLOT_LONG类型,因此调用形式必须是curl_easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L)这样的long值。

模式语法:fnmatch 风格的通配符

开启通配符后,URL 的最后一段(文件名部分)按 shell 模式匹配规则解析。libcurl 默认使用自己的内部匹配实现;若系统提供了fnmatch,也可选择调用系统实现(见下文"底层实现"一节)。以下语法说明完整摘自原手册。

*星号

匹配任意多个字符(包括零个)。例如:

ftp://example.com/some/path/*.txt

匹配该目录下所有.txt文件。注意限制:同一个模式字符串中最多只允许出现两个星号。

?问号

匹配任意恰好一个字符。例如:

ftp://example.com/some/path/photo?.jpg

可匹配photo1.jpgphotoa.jpg等,但不会匹配photo.jpg(缺少一个字符)或photo10.jpg(多出一个字符)。

[左方括号:括号表达式

左方括号开启一个括号表达式(bracket expression),表达式以右方括号]结束,整体匹配恰好一个字符。括号表达式内部,?*不再具有特殊含义,只按普通字符处理。支持以下几种写法:

  • 字符区间[a-zA-Z0-9][f-gF-G],匹配区间内的任意一个字符。
  • 字符枚举[abc],匹配abc中的任意一个。
  • 否定[^abc][!abc],匹配除abc之外的任意一个字符。
  • 字符类(class expression)[[:name:]],支持以下 11 个类:alnumlowerspacealphadigitprintupperblankgraphxdigit
  • 特殊情况[][-!^]匹配-][!^这五个字面字符——它们在括号表达式中不承担特殊用途,仅按普通字符处理。
  • 转义语法[[]]用于匹配[]e

综合运用上述规则可以构造复杂模式,例如:

ftp://example.com/some/path/[a-z[:upper:]\\].jpg

该模式要求文件名第一个字符是小写字母、大写字母(通过[:upper:]类)、[]\\\表示转义后的反斜杠),随后紧跟.jpg后缀。

底层实现

curl 仓库的 lib/curl_fnmatch.c 提供了上述语法的内部实现,其函数声明位于 lib/curl_fnmatch.h:

int Curl_fnmatch(void *ptr, const char *pattern, const char *string);

从源码结构可以推断出以下实现细节:

  • 该文件头部注释(lib/curl_fnmatch.c第 30 行附近)说明它"以递归回溯(recursive backtracking)方式实现";#ifndef HAVE_FNMATCH表明:当平台自带fnmatch(如 glibc 提供的 POSIXfnmatch(3))时,可以编译期切换到系统实现;否则使用 curl 内置版本,这保证了在不同嵌入式与桌面平台上的可移植性。
  • 内置实现通过一组常量把字符类映射为"伪字符",例如CURLFNM_ALNUMCURLFNM_DIGITCURLFNM_XDIGITCURLFNM_ALPHACURLFNM_PRINTCURLFNM_BLANKCURLFNM_LOWERCURLFNM_GRAPHCURLFNM_SPACE等(lib/curl_fnmatch.c),它们与手册中列出的[[:name:]]字符类一一对应,正是这些类的底层支撑。
  • CURLFNM_CHSET_SIZE定义为256 + 15字节的字符集位图,用于在括号表达式内快速判定字符是否命中区间/枚举/否定集合。

回调协作:过滤与分段处理

通配符传输并不是简单地一次性拉取全部文件,而是通过三个回调与 libcurl 协作,实现"逐个文件决策 + 逐个文件下载"的流程:

选项回调时机
CURLOPT_CHUNK_BGN_FUNCTION每个具体文件开始下载之前被调用
CURLOPT_CHUNK_END_FUNCTION每个文件的数据传输结束之后被调用
CURLOPT_FNMATCH_FUNCTION用自定义匹配逻辑替代内部模式匹配(可选)

这些回调指针存储在 lib/urldata.h 的chunk_bgnchunk_endfnmatchfnmatch_datawildcardptr字段中,并在 lib/setopt.c 中分别登记。

起始回调curl_chunk_bgn_callback

typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, void *ptr, int remains);

定义于 include/curl/curl.h。transfer_info携带远程文件信息(可转型为struct curl_fileinfo *查看文件名、大小、mtime 等),ptrCURLOPT_WILDCARDPTR传入的用户指针,remains表示列表中剩余文件数量。返回值含义(include/curl/curl.h):

返回值含义
CURL_CHUNK_BGN_FUNC_OK(0)正常下载当前文件
CURL_CHUNK_BGN_FUNC_FAIL(1)中止整个通配符任务
CURL_CHUNK_BGN_FUNC_SKIP(2)跳过当前文件,继续处理下一个

结束回调curl_chunk_end_callback

typedef long (*curl_chunk_end_callback)(void *ptr);

定义于 include/curl/curl.h(同一行号区域),在单个文件传输结束后调用,可用于统计下载数量、汇总大小、记录日志等。

自定义匹配回调curl_fnmatch_callback

typedef int (*curl_fnmatch_callback)(void *ptr, const char *pattern, const char *string);

定义于 include/curl/curl.h。当CURLOPT_FNMATCH_FUNCTION被设置时,libcurl 不再使用内部匹配实现,而是把模式与每个远程文件名交给该回调裁决。返回CURL_FNMATCHFUNC_MATCH(0) 表示"文件名匹配模式"(include/curl/curl.h),应下载;返回非匹配值则跳过。这为需要大小写不敏感匹配、正则式扩展匹配或白名单过滤的场景提供了灵活性。

完整示例

原手册给出的示例骨架如下,它展示了通配符选项与两个块回调的组合:

extern long begin_cb(struct curl_fileinfo *, void *, int); extern long end_cb(void *ptr); int main(void) { CURL *curl = curl_easy_init(); if(curl) { /* turn on wildcard matching */ curl_easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); /* callback is called before download of concrete file started */ curl_easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, begin_cb); /* callback is called after data from the file have been transferred */ curl_easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, end_cb); /* See more on https://curl.se/libcurl/c/ftp-wildcard.html */ } }

将其扩展为可运行版本,需要补上 URL、写文件回调与用户指针:

#include <curl/curl.h> #include <stdio.h> /* 每个文件下载前调用:remains 为剩余文件数 */ static long begin_cb(const void *transfer_info, void *ptr, int remains) { const struct curl_fileinfo *fi = transfer_info; printf("开始下载: %s (剩余 %d 个文件)\n", fi->filename, remains); return CURL_CHUNK_BGN_FUNC_OK; } /* 每个文件下载完成后调用 */ static long end_cb(void *ptr) { long *count = ptr; (*count)++; printf("完成下载,累计 %ld 个文件\n", *count); return CURL_CHUNK_END_FUNC_OK; } int main(void) { CURL *curl = curl_easy_init(); long done = 0; if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/some/path/*.txt"); curl_easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); curl_easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, begin_cb); curl_easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, end_cb); curl_easy_setopt(curl, CURLOPT_WILDCARDPTR, &done); /* 还可设置 CURLOPT_WRITEFUNCTION/CURLOPT_WRITEDATA 决定文件落盘方式 */ CURLcode res = curl_easy_perform(curl); if(res != CURLE_OK) fprintf(stderr, "wildcard transfer failed: %s\n", curl_easy_strerror(res)); curl_easy_cleanup(curl); } return 0; }

注意:CURL_CHUNK_END_FUNC_OKCURL_CHUNK_BGN_FUNC_OK均为 0,代表正常继续。编译时链接-lcurl即可。

底层状态机:一次批量传输如何运转

通配符传输在 libcurl 内部由一个有限状态机(FSM)驱动,实现在 lib/ftp.c 的ftp_do_more相关逻辑中(约 lib/ftp.c)。从源码看,struct WildcardData是保存匹配状态的载体(lib/urldata.h),其状态流转大致如下:

  1. CURLWC_CLEAN:初始状态。libcurl 从 URL 的最后一个/之后提取模式串(wildcard->pattern = curlx_strdup(last_slash)),并分配 FTP 协议专用的通配符数据(wildcard->ftpwc)。
  2. CURLWC_MATCHING:发送 FTP 列表命令,收集远程目录条目,逐一与模式比对,命中者进入文件列表(wildcard->filelist链表)。
  3. CURLWC_DOWNLOADING:从链表头部依次取出文件,拼接完整路径后发起下载;下载前调用chunk_bgn回调——若返回CURL_CHUNK_BGN_FUNC_SKIP则状态切到CURLWC_SKIP,若返回CURL_CHUNK_BGN_FUNC_FAIL则整体失败;下载完成后调用chunk_end回调,并移除链表节点(lib/ftp.c)。
  4. CURLWC_SKIP / CURLWC_DONE / CURLWC_ERROR:跳过、全部完成或出错时的终止状态;结束时通过wildcard->dtor释放协议数据。

data->state.wildcardmatch为真且状态机进入CURLWC_SKIPCURLWC_DONE时,后续传输逻辑会被短路(lib/ftp.c),同时ftp_done_wildcard(lib/ftp.c)负责在每次传输收尾时清理并调用chunk_end。这一设计解释了为何CHUNK_END_FUNCTION会在"整个批次结束"时也被调用一次,用于收尾统计。

注意事项

  • 通配符模式只能出现在 URL 末尾的文件名部分,不能对中间路径段使用(模式以最后一个/为界提取)。
  • 同一个模式字符串内最多两个星号,超出部分会被拒绝或无法按预期匹配。
  • 选项仅对 FTP 生效;对其他协议(HTTP、SFTP 等)设置该选项不会启用批量匹配。
  • 开启通配符后,若同时使用CURLOPT_FTP_SKIP_PASV_IP之类的 FTP 选项,需要注意通配符路径与CURLOPT_NOCWD的兼容性(源码注释明确"wildcard does not support NOCWD option",见 lib/ftp.c 附近)。
  • 若不想依赖内部实现,可通过CURLOPT_FNMATCH_FUNCTION提供自定义匹配函数,匹配语义完全由回调决定。

返回值

curl_easy_setopt(CURL *handle, CURLOPT_WILDCARDMATCH, ...)返回CURLcodeCURLE_OK(0) 表示设置成功,非零值表示出错,具体错误码参见libcurl-errors(3)

延伸阅读

  • CURLOPT_URL 手册:模式所在的 URL 如何构造
  • CURLOPT_CHUNK_BGN_FUNCTION 手册 与 CURLOPT_CHUNK_END_FUNCTION 手册:两个块回调的完整签名与返回码约定
  • CURLOPT_FNMATCH_FUNCTION 手册:自定义模式匹配的入口
  • 源码:lib/ftp.c(FSM 驱动)、lib/curl_fnmatch.c(内部匹配器)、lib/urldata.h(状态与回调存储)

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 16:49:07

MuJoCo 机械臂抓取仿真不稳?指尖几何、摩擦与求解器怎么调

MuJoCo 机械臂抓取仿真不稳&#xff1f;指尖几何、摩擦与求解器怎么调 【免费下载链接】mujoco Multi-Joint dynamics with Contact. A general purpose physics simulator. 项目地址: https://gitcode.com/GitHub_Trending/mu/mujoco 手合拢、物体却从指尖滑走&#xf…

作者头像 李华
网站建设 2026/9/11 16:47:45

Vosk 完整指南:一条命令跑起零延迟离线语音识别

Vosk 完整指南&#xff1a;一条命令跑起零延迟离线语音识别 【免费下载链接】vosk-api Offline speech recognition API for Android, iOS, Raspberry Pi and servers with Python, Java, C# and Node 项目地址: https://gitcode.com/GitHub_Trending/vo/vosk-api Vosk …

作者头像 李华
网站建设 2026/9/11 16:47:37

Claude Code:AI驱动的CLI Agent如何提升开发效率

1. Claude Code 为何成为开发者新宠&#xff1f;最近在开发者社区中&#xff0c;Claude Code 的热度持续攀升&#xff0c;几乎每个技术论坛都能看到相关讨论。作为一名长期关注AI工具落地的开发者&#xff0c;我最初也对这种狂热持怀疑态度&#xff0c;直到亲自体验后才理解其价…

作者头像 李华
网站建设 2026/9/11 16:44:59

5 分钟把 Visio 文件批量导出成 PNG(drawio-desktop 实操)

5 分钟把 Visio 文件批量导出成 PNG&#xff08;drawio-desktop 实操&#xff09; 【免费下载链接】drawio-desktop Official electron build of draw.io 项目地址: https://gitcode.com/GitHub_Trending/dr/drawio-desktop 同事发来一个 .vsdx 架构图&#xff0c;你的 …

作者头像 李华
网站建设 2026/9/11 16:41:21

Java土地档案系统:业务复杂度落地与Spring MVC工程实践

简介&#xff1a;本资源是一套完整的Java土地档案管理系统毕业设计实践包&#xff0c;面向计算机专业本科生及Java初学者&#xff0c;聚焦企业级Web应用开发全流程训练。压缩包共136.21MB&#xff0c;包含项目报告、答辩PPT、可运行源代码、MySQL数据库脚本及系统部署实操视频&…

作者头像 李华