必须设置curlopt_upload为1l启用ftp上传,否则失败;需配对设置curlopt_readfunction和curlopt_readdata;url须含完整文件路径;大文件建议显式设curlopt_infilesize_large;多文件应使用curl_multi_perform。

curl_easy_setopt设置FTP上传参数时容易忽略的必需项
libcurl默认不启用FTP上传,必须显式设置CURLOPT_UPLOAD为1L,否则调用curl_easy_perform会直接返回CURLE_FAILED_INIT或静默失败。仅设CURLOPT_URL为ftp://地址是不够的。
上传单个文件还需配对设置:CURLOPT_READFUNCTION(提供数据读取回调)和CURLOPT_READDATA(传入文件指针或缓冲区)。若漏掉任一,libcurl会报CURLE_READ_ERROR。
-
CURLOPT_URL必须含完整路径,如"ftp://user:pass@host/path/to/file.txt";路径末尾不能是/,否则libcurl当作目录创建而非文件上传 -
CURLOPT_INFILESIZE_LARGE建议显式设置,尤其文件大于2GB时;不设可能触发CURLE_UPLOAD_FAILED(libcurl 7.60+ 默认尝试自动探测,但不可靠) - FTP服务器若需被动模式(PASV),需确保
CURLOPT_FTP_USE_EPSV设为1L(默认开启),但内网NAT环境常需关掉:curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 0L)
多文件上传必须用curl_multi_perform而非循环调用curl_easy_perform
逐个调用curl_easy_perform上传多个文件是串行阻塞的,且每个连接都要重连FTP控制通道,效率极低。正确做法是复用同一个CURLM *句柄,为每个文件创建独立的CURL *实例并加入multi handle。
关键点在于:每个CURL *实例需单独配置URL、读回调、读数据等,但可共享登录凭据(通过CURLOPT_USERPWD或URL内嵌)和FTP连接复用策略(CURLOPT_FTP_USE_EPRT、CURLOPT_TCP_KEEPALIVE等)。
- 每个
CURL *必须调用curl_easy_setopt(curl, CURLOPT_PRIVATE, &file_info)绑定自定义结构体,以便在CURLOPT_WRITEFUNCTION或完成回调中识别上下文 - 务必在
curl_multi_add_handle前设置好所有选项,添加后修改选项无效 - 上传完成后用
curl_multi_remove_handle及时清理,否则句柄泄漏;不要在回调里直接调用curl_easy_cleanup,应在主循环中统一处理
FTP上传回调函数中FILE*指针传递与生命周期管理
常见错误是把栈上声明的FILE*(如FILE* fp = fopen("a.txt", "rb"))直接传给CURLOPT_READDATA,然后在回调中使用。若文件在上传中途被fclose或作用域结束,后续读取将崩溃。
正确做法是:将FILE*封装进自定义结构体,作为CURLOPT_READDATA值传入,并在CURLOPT_READFUNCTION回调中强转回结构体指针;上传结束后由调用方统一关闭文件。
struct UploadContext {
FILE* fp;
size_t remaining;
};
size_t read_callback(void* ptr, size_t size, size_t nmemb, void* userp) {
auto ctx = static_cast<uploadcontext>(userp);
size_t to_read = std::min(ctx->remaining, size * nmemb);
size_t n = fread(ptr, 1, to_read, ctx->fp);
ctx->remaining -= n;
return n;
}</uploadcontext>
- 不要在
read_callback里调用fseek或rewind,libcurl不保证回调调用顺序和次数 - 若文件已读完但libcurl仍调用回调,应返回0,否则可能重复发送末尾垃圾数据
- Windows下用
fopen(..., "rb"),Linux/macOS也建议统一用二进制模式,避免换行符转换干扰
错误码CURLE_PARTIAL_FILE和CURLE_COULDNT_CONNECT的实际含义
CURLE_PARTIAL_FILE不是网络中断,而是libcurl期望写入N字节,但你的read_callback只返回了M字节(M ctx->remaining计算错误或文件提前EOF导致。
CURLE_COULDNT_CONNECT在FTP场景下大概率是PASV模式端口被防火墙拦截,或服务器返回的PASV IP地址是内网地址(如192.168.x.x),客户端无法直连。此时必须关掉EPSV/EPSV并手动指定FTP传输IP(CURLOPT_FTPPORT)或改用主动模式(CURLOPT_FTP_USE_PORT)。
- 调试时开启
CURLOPT_VERBOSE,输出能看到PASV响应的IP和端口,确认是否可达 - 企业级FTP服务器常禁用PORT模式,需提前和运维确认协议策略
- libcurl 7.71.0+ 支持
CURLOPT_FTP_SKIP_PASV_IP跳过PASV响应中的IP字段,强制用控制连接IP,可缓解NAT问题
多文件FTP上传真正难的不是语法,而是每个CURL *的上下文隔离、连接复用边界、以及错误码背后的真实网络语义。别信“设完URL就能传”的例子,FTP协议状态机比HTTP复杂得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











