curl 命令行 `--upload-file`(-T):把本地文件上传到远端 URL 的完整技术指南
2026/9/10 23:53:52 网站建设 项目流程

curl 命令行--upload-file(-T):把本地文件上传到远端 URL 的完整技术指南

【免费下载链接】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 命令行选项--upload-file(短形式-T)展开,基于 官方文档 并结合 curl 源码实现,完整讲解上传文件时的 URL 文件名补全规则、stdin 上传(-.)的区别、HTTP 下自动切换 PUT 方法的底层机制,以及多文件 glob 批量上传与 8.21.0 引入的具名 glob 用法。读完后你可以直接用-T完成 FTP、HTTP(S)、IMAP、SMTP 等协议下的文件上传,并能从源码层面理解 curl 是如何配对-T与 URL 的。

选项速览

属性
长选项--upload-file
短选项-T
参数<file>(本地文件路径,-/.表示 stdin,空参数表示“不上传”)
作用将本地文件上传到指定 URL 指向的远端位置
引入版本curl 4.0
作用粒度per-URL(每个-T与一个 URL 配对)
所属分类important / upload / imap
相关选项--get--head--request--data

该选项在 curl 的参数表中注册于 src/tool_getparam.c:

{"upload-file", ARG_FILE, 'T', C_UPLOAD_FILE},

ARG_FILE表明它接收一个文件参数,'T'即短形式-T。文档同时列出它属于important类(帮助输出中会重点展示的核心上传选项),并标注了imap分类——用-T上传邮件正文是 IMAP 协议的典型用法。

基本用法与示例

文档给出的全部示例均可直接复制运行:

# 1. 最基础:把本地 file 上传到 $URL curl -T file $URL # 2. FTP:用范围 glob 上传 1000 张图片 curl -T "img[1-1000].png" ftp://ftp.example.com/ # 3. HTTP:把多个文件上传到同一 URL(注意 URL 的 glob 写法) curl --upload-file "{file1,file2}" $URL # 4. 每个 -T 与一个 URL 一一对应 curl -T file -T file2 $URL $URL

要点说明:

  • 一个-T对一个 URL:命令行上可以出现多个--upload-file,它们按出现顺序与 URL 列表一一配对,分别决定“上传什么、上传到哪里”。这正是文档元数据中Multi: per-URL的含义。
  • glob 支持--upload-file的参数本身支持 glob 展开({1,2,3}[1-1000]等与 URL 侧同一套 glob 语法),因此可以把一组本地文件上传到同一个远端位置。
  • 在 verbose 输出(-v)中,curl 会把这种请求标注为PUT (-T, --upload-file),见 src/tool_helpers.c。

源码视角:-T 与 URL 如何配对

-T的解析函数是parse_upload_file(),位于 src/tool_getparam.c。它的核心逻辑是:

  1. 维护一个config->url_ul节点指针(getout节点链表),始终指向第一个“尚未被-T占用”的 URL 节点;
  2. 若当前节点已有uploadset标记,则沿链表后移寻找空闲节点;
  3. 没有空闲节点时通过new_getout(config)新建一个;
  4. 命中后设置url->uploadset = TRUE并保存文件名到url->infileDENY_BLANK表示参数不能为空白;-被允许保留为字符串,后续再按 stdin 处理)。
/* src/tool_getparam.c:1494 附近 */ if(!config->url_ul) config->url_ul = config->url_list; if(config->url_ul) { /* 跳过已填充的节点,寻找空闲节点 */ while(config->url_ul && config->url_ul->uploadset) config->url_ul = config->url_ul->next; } ... url->uploadset = TRUE; /* mark -T used */ if(!*nextarg) url->noupload = TRUE; /* 空参数 = 显式取消该 URL 的上传 */ else err = getstr(&url->infile, nextarg, DENY_BLANK);

从这段代码可以确认两件事:-T的配对是严格的顺序配对;另外--upload-file ''(空参数)会被解析为noupload = TRUE,即显式声明“该 URL 不上传”。

URL 没有文件名时:自动追加本地文件名

这是--upload-file最容易踩坑的行为,文档原文明确规定:

If there is no file part in the specified URL, curl appends the local file name to the end of the URL before the operation starts. You must use a trailing slash (/) on the last directory to prove to curl that there is no filename or curl thinks that your last directory name is the remote filename to use.

也就是说:

  • 如果 URL 路径没有文件名部分(以/结尾或无路径),curl 会在操作开始前把本地文件名追加到 URL 末尾作为远端文件名;
  • 当最后一级是目录时,必须用结尾斜杠/来证明它是目录,否则 curl 会把最后一段当作远端文件名。例如ftp://host/pub/ftp://host/pub的上传目标文件名不同。

同时文档规定:追加时 curl 只取本地路径中最右侧/\右边的部分,左侧路径一律忽略。因此curl -T /tmp/report.xlsx ftp://ftp.example.com/的远端文件名是report.xlsx而非/tmp/report.xlsx

源码视角:add_file_name_to_url()

这一行为由 src/tool_operhlp.c 中的add_file_name_to_url()实现,它在上传流程的setup_transfer_upload()(src/tool_operate.c)中被调用:

/* src/tool_operate.c:1324 附近 */ if(per->uploadfile) { if(stdin_upload(per->uploadfile)) check_stdin_upload(config, per); else { /* We have specified a file to upload and it is not "-" */ result = add_file_name_to_url(per->curl, &per->url, per->uploadfile);

add_file_name_to_url()的关键步骤(见 src/tool_operhlp.c):

  1. 用 CURLU 句柄解析 URL,分别取出CURLUPART_PATHCURLUPART_QUERY
  2. 若 URL 带 query(?之后有内容),直接不做修改并返回——避免破坏带参数的 URL;
  3. 检查路径最后一个/之后是否还有字符:ptr = strrchr(path, '/'); if(!ptr || !*++ptr)表示“没有文件名部分”,才进入追加逻辑;
  4. 追加时执行文档所述的“取最右斜杠后部分”规则,且同时兼容 Windows 反斜杠
/* 只取最右侧 / 或 \ 右边的部分 */ const char *filep = strrchr(filename, '/'); const char *file2 = strrchr(filep ? filep : filename, '\\'); ... /* 文件名做 URL 编码后拼接到路径上 */ encfile = curl_easy_escape(curl, filep, 0); newpath = curl_maprintf("%s%s", path, encfile); /* path 以 / 结尾时 */

注意追加前会用curl_easy_escape()对文件名做 URL 编码,所以本地文件名含空格、中文等字符时会被正确转义,不需要手动处理。

用 stdin 上传:-.的区别

文档规定两种特殊文件名:

  • -(短横线):用 stdin 代替文件上传;
  • .(句点):同样使用 stdin,但是非阻塞模式,允许在上传 stdin 的同时读取服务端输出。

源码中对这两种符号的判定集中在 src/tool_operhlp.c:

bool stdin_upload(const char *uploadfile) { return !strcmp(uploadfile, "-") || !strcmp(uploadfile, "."); }

-.的差异体现在 src/tool_operate.c 的非阻塞分支,以及check_stdin_upload()(src/tool_operate.c)中的一条重要告警:

/* src/tool_operate.c:1184 附近 */ warnf("Using --anyauth or --proxy-anyauth with upload from stdin" " will make the transfer stop at the first unauthenticated" " response.");

也就是说:stdin 上传 +--anyauth(或--proxy-anyauth)组合会使传输在收到第一个未认证响应时停止——因为认证重试需要重放请求,而 stdin 只能消费一次。对交互式认证场景,应优先用具体认证方式(如--basic--digest)代替--anyauth

典型 stdin 上传用法:

# 把文件内容经管道/重定向从 stdin 上传 cat report.xlsx | curl -T - ftp://ftp.example.com/report.xlsx # 非阻塞 stdin 上传(.),上传期间可同时读服务端回显 curl -T . https://upload.example/ < bigfile.bin

HTTP(S) 下的行为:自动使用 PUT

文档明确写道:“If this option is used with an HTTP(S) URL, the PUT method is used.” 这条规则在 libcurl 命令行层的实现链路是:

  1. src/config2setopts.c 中,只要该 URL 配了上传文件,就开启CURLOPT_UPLOAD
my_setopt_long(curl, CURLOPT_UPLOAD, !!per->uploadfile);
  1. libcurl 内部据此把 HTTP 方法设为 PUT。在 lib/http.c 可以看到httpreq = HTTPREQ_PUT;的赋值,随后在 lib/http.c 的case HTTPREQ_PUT: /* Let's PUT the data to the server! */分支中真正发送 PUT 请求。

因此不需要再配合--request PUT-T本身就隐含了 PUT;而文档 See-also 中列出的--request可用于显式覆盖(例如用--request POST强制 POST 上传)。

文件大小如何告知服务器

上传前 curl 会探测本地文件大小并设置为CURLOPT_INFILESIZE_LARGE,这样 HTTP PUT 能携带准确的Content-Length。相关逻辑在 src/tool_operate.c:

/* src/tool_operate.c:268 附近(VMS 特判从略) */ if(curlx_stat(per->uploadfile, &fileinfo) == 0) { ... per->infd = curlx_open(per->uploadfile, O_RDONLY | CURL_O_BINARY); ... uploadfilesize = fileinfo.st_size; } ... if(uploadfilesize != -1) my_setopt_offt(per->curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);

文件以二进制模式(CURL_O_BINARY)打开,保证了 FTP 二进制传输与 HTTP 原始字节上传的完整性;stat 失败(如 stdin、管道)时uploadfilesize保持 -1,表示长度未知,这与-/.stdin 上传的“流式”语义一致。

多文件与 glob 批量上传

--upload-file的参数支持 URL 同款 glob,把多个本地文件打到同一个远端位置:

# 三个文件上传到同一 FTP 目录(远端文件名各自独立) curl --upload-file 'file{1,2,3}' ftp://ftp.example/ # 文档元数据中的等价写法 curl --upload-file "{file1,file2}" $URL

上传侧 glob 的推进发生在 src/tool_operate.c:当某个 URL 对应的本地文件名用完后,通过glob_next_url(&state->uploadfile, &state->inglob)取下一个 glob 展开项,直到全部上传完毕。

8.21.0 新增:具名 glob(named globs)

自 curl 8.21.0 起,上传文件名 glob 支持命名,并在同一命令行中被其他选项引用——引用方式与 URL 侧的具名 glob 完全相同。文档给出的例子是把三个文件上传到同一个固定 HTTP URL,并把各自的响应分别存到不同文件:

curl -T 'file{<num>1,2,3}' \ https://upload.example/ -o 'response-#<num>'

解释:

  • file{<num>1,2,3}是具名 glob,<num>是变量名,展开时依次取值 1、2、3;
  • -o 'response-#<num>'中的#<num>引用同一个变量,于是三次传输分别写入response-1response-2response-3
  • 上传目标https://upload.example/是固定的,不随 glob 变化。

这解决了“批量上传同一端点、分别保存响应”这类此前难以在单条命令内表达的需求。

使用 SMTP 时的格式要求

文档最后一段针对 SMTP 上传(即“发邮件”场景)给出硬性约束:

When uploading to an SMTP server (aka "sending email"): the uploaded data is assumed to be RFC 5322 formatted. It has to feature the necessary set of headers and mail body formatted correctly by the user as curl does not transcode nor encode it further in any way.

即:上传给 SMTP 服务器的数据必须已经是完整的 RFC 5322 邮件格式(含必要的头部与正文),curl 不会做任何转码、编码或格式补全。准备本地文件时应自行写好From:To:Subject:等头与正文。

常见问题与注意事项

  1. 结尾斜杠决定远端文件名ftp://host/dir会把dir当作远端文件名(当 URL 已有文件名部分时,本地文件名被忽略);要上传到dir目录下且保留本地文件名,必须写ftp://host/dir/。这是由add_file_name_to_url()中“仅当最后/后无字符才追加”的判定逻辑直接决定的(见前文源码分析)。
  2. 本地路径只取文件名部分C:\data\a\report.txt/data/a/report.txt上传后的远端名都只有report.txt;Windows 反斜杠分隔符在 src/tool_operhlp.c 中被显式处理。
  3. stdin 只能消费一次:配合--anyauth会出现“认证重试失败即停止”的告警行为(src/tool_operate.c),交互式认证请避免该组合。
  4. HTTP 下是 PUT 而非 POST:需要 POST 上传时用--data/-F系列选项;--upload-file的语义就是“把整个本地文件按原字节流 PUT/上传”。
  5. per-URL 配对-T a -T b URL1 URL2a→URL1b→URL2;想上传多个文件到同一 URL,用 glob(file{1,2})而不是重复 URL。

相关文档与源码索引

  • 本文档原型:docs/cmdline-opts/upload-file.md —— 实际路径为 docs/cmdline-opts/upload-file.md
  • 参数注册与-T/URL 配对解析:src/tool_getparam.c
  • URL 文件名追加与 Windows 路径处理:src/tool_operhlp.c
  • 上传流程编排、stdin 处理与文件大小探测:src/tool_operate.c、src/tool_operate.c
  • CURLOPT_UPLOAD到 PUT 的映射:src/config2setopts.c、lib/http.c
  • 上传缓冲区与上传状态字段定义:lib/urldata.h

适用前提:本文内容以当前仓库(curl 主仓库)文档与源码为准;具名 glob 示例要求 curl ≥ 8.21.0。其余-T基础行为自 curl 4.0 起长期稳定。

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

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

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

立即咨询