☰
curl 的 CURLOPT_APPEND 选项详解:实现 FTP/SFTP 上传文件追加(Append)模式
2026/10/18 6:38:32 网站建设 项目流程

curl 的 CURLOPT_APPEND 选项详解:实现 FTP/SFTP 上传文件追加(Append)模式

【免费下载链接】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_APPEND 是 libcurl 提供的一个上传控制选项:把它置为 1,可以让上传操作在远程文件末尾追加数据,而不是覆盖整个文件。该选项在 7.17.0 加入,适用于 FTP 与 SFTP 两种协议,常用于日志归集、增量备份、断点续传等场景。本文将以 docs/libcurl/opts/CURLOPT_APPEND.md 为骨架,结合 curl 仓库中的源码实现(lib/setopt.c、lib/ftp.c、lib/vssh/libssh2.c 等),完整讲解该选项的用法、底层命令差异、与 CURLOPT_RESUME_FROM 的关系,以及命令行工具中的对应开关,帮助你准确掌握追加上传的实战细节。

选项速览

项目内容
选项名CURLOPT_APPEND
所属句柄CURL *(通过curl_easy_setopt设置)
参数类型long,0 或 1(布尔语义)
适用协议FTP、SFTP
加入版本7.17.0(此前名称为 CURLOPT_FTPAPPEND)
默认值0(禁用,即覆盖模式)

函数原型与基本用法

CURLOPT_APPEND 的完整函数原型如下(见 docs/libcurl/opts/CURLOPT_APPEND.md 的 SYNOPSIS 小节):

#include <curl/curl.h> CURLcode curl_easy_setopt(CURL *handle, CURLOPT_APPEND, long append);
  • handle:通过curl_easy_init()创建的 easy 句柄;
  • append:传1L表示追加,传0L表示覆盖(默认行为)。

原文档给出的最小可运行示例(FTP 场景)如下:

int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/dir/to/newfile"); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl, CURLOPT_APPEND, 1L); result = curl_easy_perform(curl); curl_easy_cleanup(curl); } }

这里有三点需要特别注意:

  1. 必须配合 CURLOPT_UPLOAD:CURLOPT_APPEND 只在“上传”语义下有意义,单独设置它不会产生传输动作;
  2. 目标文件可以不存在:追加模式下若远程文件尚不存在,大多数服务器会直接创建它(SFTP 实现尤其明确,见下文源码分析);
  3. 返回值:curl_easy_setopt始终返回CURLE_OK (0)表示设置成功,非零值表示出错,具体错误码参见 libcurl-errors(3) 文档。

选项在 libcurl 内部的落点

从源码看,该选项的解析入口在 lib/setopt.c:

case CURLOPT_APPEND: /* * We want to upload and append to an existing file. Used for FTP and * SFTP. */ s->remote_append = enabled; break;

它把布尔值写入struct Curl_easy的remote_append位域(定义于 lib/urldata.h),供各协议后端在上传初始化阶段读取。同时,在选项表中(lib/easyoptions.c)它被登记为CURLOT_LONG类型的长整型选项。

底层实现:FTP 的 STOR 与 APPE

FTP 上传时,覆盖与追加对应两个不同的 FTP 控制命令:

  • STOR <filename>:存储(覆盖)文件;
  • APPE <filename>:追加(append)到文件末尾。

在 lib/ftp.c 的ftp_state_ul_setup()函数中,libcurl 读取data->set.remote_append决定发送哪条命令:

curl_bit append =>if(data->set.remote_append) { /* True append mode: create if nonexisting */ flags = LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_APPEND; } else if(data->state.resume_from > 0) { /* * Resume MUST NOT use APPEND; some servers force writes to EOF when * APPEND is set, ignoring a prior seek(). */ flags = LIBSSH2_FXF_WRITE; } else { /* Clear file before writing (normal behavior) */ flags = LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC; }

从这段代码可以归纳出三个明确的行为:

  1. 追加模式:使用WRITE | CREAT | APPEND,文件不存在时会自动创建;
  2. 续传模式(CURLOPT_RESUME_FROM):使用纯WRITE,明确不使用 APPEND,因为某些 SFTP 服务器在 APPEND 标志下会强制写向 EOF 而忽略 seek 偏移;
  3. 默认模式:使用WRITE | CREAT | TRUNC,先清空再写入,即覆盖行为。

libssh 后端在 lib/vssh/libssh.c 中也有同样的data->set.remote_append判断逻辑,说明追加语义在两条 SFTP 实现路径上保持一致。

与 CURLOPT_RESUME_FROM 的关系

原文档的 See-also 小节同时列出了 CURLOPT_DIRLISTONLY、CURLOPT_RESUME_FROM 和 CURLOPT_UPLOAD,其中 CURLOPT_RESUME_FROM 与追加模式关系最密切:

  • CURLOPT_APPEND:从远程文件当前末尾继续写入,本地数据流从头开始读取;
  • CURLOPT_RESUME_FROM:从本地数据流的指定偏移处开始读取,配合 FTP 续传时,libcurl 会先用SIZE查询远程文件大小,再跳过本地相应字节数。

有趣的是,两者在 FTP 实现中会“合流”。在 lib/ftp.c 的ftp_state_ul_setup()中可以看到:当设置了resume_from时,libcurl 会把append变量强制置为TRUE,随后同样发送APPE命令,并跳过本地数据流前resume_from个字节——用追加代替REST偏移命令完成断点续传。原注释也明确写道:“This used to set REST, but since we can do append, we issue no another ftp command.”

而在 SFTP 侧(lib/vssh/libssh2.c),resume_from与remote_append则是互斥的两条路径:续传走seek + WRITE,追加走APPEND标志,二者不会叠加。

命令行工具中的对应开关

curl 命令行工具通过--append(短选项-a)暴露该能力,其参数解析在 src/tool_getparam.c 与 src/tool_getparam.c:

{"append", ARG_BOOL, 'a', C_APPEND}, ... case C_APPEND: /* --append */ config->ftp_append = toggle;

命令行中设置后,工具最终通过 src/config2setopts.c 把配置映射为 libcurl 选项:

my_setopt_long(curl, CURLOPT_APPEND, config->ftp_append);

于是curl --append --upload-file local.txt ftp://example.com/remote.txt(或curl -a -T local.txt ftp://example.com/remote.txt)就等价于在代码中设置 CURLOPT_APPEND=1。注意命令行开关与 API 选项一样,仅对 FTP/SFTP 上传生效,对其他协议会被忽略。

历史与别名

该选项的历史沿革(见原文档 HISTORY 小节):

  • 在 7.16.4 及更早版本中,它叫CURLOPT_FTPAPPEND;
  • 从7.17.0起更名为 CURLOPT_APPEND;
  • 旧名称在选项表中仍保留为别名(lib/easyoptions.c):
{ "FTPAPPEND", CURLOPT_APPEND, CURLOT_LONG, CURLOT_FLAG_ALIAS },

因此旧代码中写CURLOPT_FTPAPPEND依然可用,但新代码建议统一使用 CURLOPT_APPEND。

返回值与错误处理

curl_easy_setopt(curl, CURLOPT_APPEND, ...)的返回值遵循 libcurl 约定:

  • CURLE_OK (0):设置成功;
  • 非零值:设置失败,具体错误码见 libcurl-errors(3)。

需要注意,设置选项成功不代表传输成功。追加操作真正失败(例如服务器不支持APPE、目录不可写、SFTP 打开文件失败)时,错误会在curl_easy_perform()阶段以协议相关错误码返回,建议在实战中同时检查curl_easy_setopt与curl_easy_perform两个返回值。

实践要点小结

  1. 适用面:仅 FTP 与 SFTP(FTPS 经 FTP 后端同样适用),其他协议忽略该选项;
  2. 与上传搭配:务必同时设置 CURLOPT_UPLOAD 并指定CURLOPT_URL指向目标文件路径;
  3. 目标文件不存在:SFTP 追加模式会自动创建文件(CREAT标志),FTP 行为取决于服务器对APPE的实现;
  4. 续传关系:FTP 下resume_from会自动切换为追加语义;SFTP 下二者互斥,续传不附加 APPEND 标志;
  5. 默认覆盖:不设置该选项时,上传总是先截断(TRUNC/STOR)再写入;
  6. 命令行等价物:curl -a -T file ftp://host/path或curl --append ...。

延伸阅读

  • 选项说明原文档:docs/libcurl/opts/CURLOPT_APPEND.md
  • 选项解析入口:lib/setopt.c、选项表与别名:lib/easyoptions.c
  • 状态字段定义:lib/urldata.h
  • FTP 的 APPE/STOR 命令选择:lib/ftp.c
  • SFTP 的打开标志逻辑:lib/vssh/libssh2.c、lib/vssh/libssh.c
  • 命令行--append参数处理:src/tool_getparam.c、src/config2setopts.c
  • 关联选项:CURLOPT_UPLOAD、CURLOPT_RESUME_FROM、CURLOPT_DIRLISTONLY

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询