transfer.sh 1.6.1 curl 上传下载命令详解

transfer.sh v1.6.1 命令行使用全解:curl 文件/管道/打包上传、Max-Days 与 Max-Downloads 限时限次、服务端与 gpg 端到端加密、GET 下载与 inline 预览、Range 分片、多文件 zip/tar 打包、wget 校验、删除令牌与脚本化嵌入,全部命令与回显实测照抄,含 405/404/429 等边界行为排障。

transfer.sh文件分享文件传输Go2026/11/01

transfer.sh 1.6.1 curl 上传下载命令详解

目标:把 transfer.sh v1.6.1 的命令行使用一次讲透——curl 上传(文件/管道/打包/过期/加密)、下载(直链/inline/分片/多文件打包/wget)、删除令牌、脚本化嵌入与全部边界行为。transfer.sh 是纯命令行型文件分享服务,curl 就是它的全部"客户端 SDK",本文所有命令与回显均实测照抄。

一、环境信息

curl 版本验证命令与实测回显:

curl --version | head -2
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.2.11 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.10
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.16

本文示例用的测试文件:

printf 'transfer.sh curl upload test\nline2 for range test' > hello.txt

二、工具概览:协议面与 URL 结构

transfer.sh 不需要专门客户端,整套协议就是三个 HTTP 动词:

操作HTTP 动词URL 形态关键请求头/参数
上传PUThttp://服务地址/文件名curl 用 --upload-file,可选 Max-Days、Max-Downloads、X-Encrypt-Password 头
下载GEThttp://服务地址/令牌/文件名(别名 /get/…、/inline/…)可选 X-Decrypt-Password 头,支持 Range
删除DELETEhttp://服务地址/令牌/文件名/删除令牌删除令牌来自上传响应头 X-Url-Delete

上传成功后响应体只有一行:该文件的下载 URL。URL 中的 令牌 是服务端生成的 10 位随机串(如 xo9KSLWTnj),每个上传不同且不可枚举;文件名 取自上传时 URL 路径里写的名字(不是本地文件名)。

下载响应的头信息里还有两个状态头(实测完整响应见 5.1):X-Remaining-Days 与 X-Remaining-Downloads,反映该链接剩余时效/剩余下载次数,未设置限制时显示 n/a。

三、前置条件

  1. 有可用的 transfer.sh 服务(自建或公开实例),curl -s http://服务地址/ | head -3 能返回说明页:
    192.168.1.172:8132: Easy file sharing from the command line
    ===
    made with <3 by DutchCoders
    
  2. 客户端有 curl 8.x(--upload-file 与 -r 分片在各版本 curl 中语义一致);
  3. 准备好测试文件(见第一章 printf 命令)。

四、上传命令详解

4.1 基础上传:--upload-file

curl -s --upload-file hello.txt http://192.168.1.172:8132/hello.txt
http://192.168.1.172:8132/xo9KSLWTnj/hello.txt

这一行输出就是分享用的下载 URL。要点:

  • --upload-file 让 curl 以 PUT 方式发送文件,等价写法是 -T hello.txt;
  • URL 路径末段的 hello.txt 是保存名,与本地文件名无关——本地叫 hello.txt、URL 路径写 todelete.txt,下载时就叫 todelete.txt(4.1 与第六章的删除实测即基于此);
  • 上传响应体只有下载 URL 一行;响应头里的删除链接需要加 -D - 才能看到,见第六章。

带响应头查看的上传(同一命令加 -D -)实测完整回显:

HTTP/1.1 200 OK
Content-Type: text/plain
Server: Transfer.sh HTTP Server
X-Made-With: <3 by DutchCoders
X-Served-By: Proudly served by DutchCoders
X-Url-Delete: http://192.168.1.172:8132/tAXL0NQUAb/todelete.txt/lNFU5oScQWGdsKe0uqdm
Date: Sun, 01 Nov 2026 04:22:43 GMT
Content-Length: 49

http://192.168.1.172:8132/tAXL0NQUAb/todelete.txt

X-Url-Delete 就是删除链接(含一次性删除令牌),只在响应头里出现,不在响应体里——想留删除能力,上传时就必须带 -D -。

4.2 管道上传:--upload-file -

--upload-file - 让 curl 从标准输入读取内容上传,任意命令的输出都能直接变成分享链接:

cat /etc/os-release | curl -s --upload-file - http://192.168.1.172:8132/os-release.txt
http://192.168.1.172:8132/vKNyDlRSUi/os-release.txt

stdin 没有文件名,所以 URL 路径里写的 os-release.txt 就是保存名,务必显式写好(含扩展名,下载方依赖它)。

4.3 目录打包上传(tar 管道)

目录不能直接上传,先打包再走管道(这也是官方 README 推荐的目录分享方式):

tar -czf - projdir | curl -s --upload-file - "http://192.168.1.172:8132/projdir.tar.gz"
http://192.168.1.172:8132/PIcvKgHpX4/projdir.tar.gz

对方下载后 tar -xzf projdir.tar.gz 还原。zip 同理:zip -r - projdir | curl -s --upload-file - http://…/projdir.zip。

4.4 上传时限制下载次数与有效期

上传时通过请求头指定该链接的寿命,实现"阅后即焚"与"限时有效":

限制下载次数(Max-Downloads)——实测 Max-Downloads: 1:

curl -s -H "Max-Downloads: 1" --upload-file hello.txt http://192.168.1.172:8132/onetime.txt
http://192.168.1.172:8132/r1VO4v159S/onetime.txt
curl -s -w ' [%{http_code}]' http://192.168.1.172:8132/r1VO4v159S/onetime.txt; echo
curl -s -w ' [%{http_code}]' http://192.168.1.172:8132/r1VO4v159S/onetime.txt; echo
transfer.sh curl upload test
line2 for range test [200]
Not Found [404]

第一次下载正常,第二次直接 404——次数用尽链接即失效。

限制有效天数(Max-Days)——实测 Max-Days: 1(一天后过期):

curl -sD - -H "Max-Days: 1" --upload-file hello.txt http://192.168.1.172:8132/maxdays1.txt
HTTP/1.1 200 OK
Content-Type: text/plain
Server: Transfer.sh HTTP Server
X-Made-With: <3 by DutchCoders
X-Served-By: Proudly served by DutchCoders
X-Url-Delete: http://192.168.1.172:8132/CLbsQuqcBz/maxdays1.txt/chu1f5HLxqpTl93Cyz1S
Date: Sun, 01 Nov 2026 07:19:15 GMT
Content-Length: 49

http://192.168.1.172:8132/CLbsQuqcBz/maxdays1.txt

两个边界的实测行为(写脚本时务必注意):

  • Max-Days 传负值会被拒绝,返回 400:
    Invalid MaxDate, make sure Max-Days is smaller than 290 years
    
    HTTP:400
    
  • Max-Days 传超出解析范围的大数(如 99999999999999999999)上传仍返回 200,但限制被静默忽略——下载响应头 X-Remaining-Days: n/a 可证。参数解析失败不报错,脚本里要对取值做前置校验;
  • Max-Downloads 传非数字(如 abc)同样被静默忽略,实测连下 3 次全部 200(等于没限制)。

过期后的下载行为实测(把某上传的有效期改为过去时间后访问):服务端返回 Not Found、HTTP 404,与"链接不存在"同一表现。

4.5 加密上传

方式一:服务端加密(X-Encrypt-Password)。上传时带密码头,文件以 AES 加密落盘,下载必须带同密码:

curl -s -H "X-Encrypt-Password: q1w2e3r4" --upload-file hello.txt http://192.168.1.172:8132/secret.txt
http://192.168.1.172:8132/r08C4PT8J2/secret.txt

下载时不带密码,拿到的是密文(PGP 报文形态):

-----BEGIN PGP MESSAGE-----

wy4ECQMI5wxylACk9czg1hEkvL5iyqTDM5jwm/BcaufuWvWgD1rJBF9Ne/zvyzYX
0mMBkMFSmjXniB1n0fc8Mm474AnXaz1zHXIJKXP6LsvQ3mBpAPX+TvZsOz1pcTOP
0yhUQY/WR98fyVHVY7kqqZ7DTq62JLzmmXckOyJzPzYHQKThmqqHajqrgF9jQIZp
AO5zUXM=
=j7aU
-----END PGP MESSAGE-----
HTTP:200

带正确密码下载(X-Decrypt-Password 头)得到明文:

curl -s -H "X-Decrypt-Password: q1w2e3r4" http://192.168.1.172:8132/r08C4PT8J2/secret.txt
transfer.sh curl upload test
line2 for range test

HTTP:200

带错误密码下载:

Could not decrypt file

HTTP:500

方式二:客户端加密(gpg 管道)。密码不经过服务器,服务端只存密文,适合更高保密要求:

printf 'transfer.sh curl upload test\nline2 for range test' | \
  gpg --batch --yes --passphrase q1w2e3r4 --symmetric --output - | \
  curl -s --upload-file - http://192.168.1.172:8132/gpgtest2.txt.gpg
http://192.168.1.172:8132/snBX0zmCC8/gpgtest2.txt.gpg

下载后本地解密还原,与原文逐字节一致:

curl -s http://192.168.1.172:8132/snBX0zmCC8/gpgtest2.txt.gpg | \
  gpg --batch --yes --pinentry-mode loopback --passphrase q1w2e3r4 --decrypt --output -
transfer.sh curl upload test
line2 for range test
DIFF: identical

gpg 两个实操细节:解密命令要带 --output -(否则 gpg 默认写文件而非标准输出,管道里表现为"解密没输出");gpg 2.x 批处理场景建议加 --pinentry-mode loopback。

五、下载命令详解

5.1 直接下载:GET

curl -s http://192.168.1.172:8132/fYdpkLW5X8/hello.txt
transfer.sh curl upload test
line2 for range test

下载后与原文比对:

diff hello.txt <(curl -s http://192.168.1.172:8132/fYdpkLW5X8/hello.txt) && echo "DIFF: identical"
DIFF: identical

完整响应头实测(下载类响应的基准形态):

HTTP/1.1 200 OK
Cache-Control: no-store
Connection: keep-alive
Content-Disposition: attachment; filename="hello.txt"
Content-Length: 50
Content-Type: 
Server: Transfer.sh HTTP Server
Vary: Range, Referer, X-Decrypt-Password
X-Made-With: <3 by DutchCoders
X-Remaining-Days: n/a
X-Remaining-Downloads: n/a
X-Served-By: Proudly served by DutchCoders
Date: Sun, 01 Nov 2026 06:49:47 GMT

三个细节:Content-Disposition: attachment 表示浏览器访问该 URL 会直接下载(而非展示);X-Remaining-Days/X-Remaining-Downloads 显示该链接剩余时效/次数(未设置时为 n/a,设置了限制后用于确认剩余额度);Vary 里的 X-Decrypt-Password 提示该头会影响响应(加密上传场景)。

5.2 /get/ 与 /inline/ 别名:控制浏览器行为

同一条上传有三种等价访问路径,仅浏览器行为不同(命令行下载三者内容一致):

路径Content-DispositionContent-Type浏览器行为
/令牌/文件名(裸路径)attachment空值直接下载
/get/令牌/文件名attachment空值直接下载
/inline/令牌/文件名inlinetext/plain; charset=utf-8页面内预览

实测三者响应头对比:

-- GET /<token>/hello.txt --
HTTP/1.1 200 OK
Cache-Control: no-store
Connection: keep-alive
Content-Disposition: attachment; filename="hello.txt"
Content-Length: 50
Content-Type: 
Server: Transfer.sh HTTP Server
Vary: Range, Referer, X-Decrypt-Password
X-Made-With: <3 by DutchCoders
X-Remaining-Days: n/a
X-Remaining-Downloads: n/a
X-Served-By: Proudly served by DutchCoders
Date: Sun, 01 Nov 2026 06:49:47 GMT

-- GET /get/<token>/hello.txt --
HTTP/1.1 200 OK
Cache-Control: no-store
Connection: keep-alive
Content-Disposition: attachment; filename="hello.txt"
Content-Length: 50
Content-Type: 
Server: Transfer.sh HTTP Server
Vary: Range, Referer, X-Decrypt-Password
X-Made-With: <3 by DutchCoders
X-Remaining-Days: n/a
X-Remaining-Downloads: n/a
X-Served-By: Proudly served by DutchCoders
Date: Sun, 01 Nov 2026 06:49:47 GMT

-- GET /inline/<token>/hello.txt --
HTTP/1.1 200 OK
Cache-Control: no-store
Connection: keep-alive
Content-Disposition: inline; filename="hello.txt"
Content-Length: 50
Content-Type: text/plain; charset=utf-8
Server: Transfer.sh HTTP Server
Vary: Range, Referer, X-Decrypt-Password
X-Made-With: <3 by DutchCoders
X-Remaining-Downloads: n/a
X-Served-By: Proudly served by DutchCoders
Date: Sun, 01 Nov 2026 06:49:47 GMT

要分享给浏览器用户直接预览(文本/图片/PDF),把 URL 改成 /inline/ 前缀即可;给脚本/命令行用则用裸路径或 /get/。

5.3 分片下载(Range)

支持 HTTP Range 请求,curl -r 即可分片,适合大文件断点续传:

curl -s -r 0-8 http://192.168.1.172:8132/fYdpkLW5X8/hello.txt
transfer.
curl -s -r 9- http://192.168.1.172:8132/fYdpkLW5X8/hello.txt
sh curl upload test
line2 for range test

-r 0-8 取前 9 字节,-r 9- 取第 10 字节到末尾,两段拼接即完整文件。Content-Range 响应头可确认分片边界,配合 curl -C - 可做简单断点续传。

5.4 多文件打包下载(.zip / .tar.gz)

把多个已上传文件合并成一个归档下载:URL 写成 /(令牌1/文件名1,令牌2/文件名2).zip——外层括号必须带上。实测:

curl -s "http://192.168.1.172:8132/(H31oeloiul/hello.txt,avp90aHDnJ/os-release.txt).zip" -o zipdl.zip -w 'HTTP:%{http_code} size:%{size_download}\n'
HTTP:200 size:738

解包验证:

unzip -l zipdl.zip
Archive:  zipdl.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
       50  2026-11-01 04:41   hello.txt
      400  2026-11-01 04:41   os-release.txt
---------                     -------
      450                     2 files

(unzip -l 清单中 Length 为归档前原文件大小,合计 450 字节;size:738 为压缩包实际字节数。)

tar.gz 同理:

curl -s "http://192.168.1.172:8132/(H31oeloiul/hello.txt,avp90aHDnJ/os-release.txt).tar.gz" -o tardl.tar.gz -w 'HTTP:%{http_code} size:%{size_download}\n'
HTTP:200 size:394
tar tzf tardl.tar.gz
hello.txt
os-release.txt

高频坑:不写括号直接访问 /令牌1/文件1,令牌2/文件2.zip 会落入服务器的删除类路由,实测返回:

HTTP:405 size:0

即 405 Method Not Allowed——GET 请求打到了只认 DELETE 的三段路径上。看到 405 先检查括号。

5.5 wget 下载与完整性校验

wget 一样可用:

wget -q http://192.168.1.172:8132/fYdpkLW5X8/hello.txt && cat hello.txt
transfer.sh curl upload test
line2 for range test

md5 双向校验(本地原文件 vs 下载文件):

md5sum hello.txt hello.txt
f269c35d5f79a12ec292846cb6327581  hello.txt
f269c35d5f79a12ec292846cb6327581  hello.txt

(第二条为 wget 重新下载后的校验,哈希一致。)脚本化分发场景建议始终附带 md5/sha256 校验,传输完整性一目了然。

六、删除与令牌

删除链接来自上传响应头的 X-Url-Delete(见 4.1 实测),形态为 下载 URL + /删除令牌。删除操作实测全流程:

curl -sD - --upload-file hello.txt http://192.168.1.172:8132/todelete.txt
(响应头同 4.1,X-Url-Delete: http://192.168.1.172:8132/tAXL0NQUAb/todelete.txt/lNFU5oScQWGdsKe0uqdm)

删除前下载正常(HTTP 200),发起 DELETE:

curl -s -X DELETE http://192.168.1.172:8132/tAXL0NQUAb/todelete.txt/lNFU5oScQWGdsKe0uqdm -w '\nHTTP:%{http_code}\n'

HTTP:200

再下载:

curl -s -w '\nHTTP:%{http_code}\n' http://192.168.1.172:8132/tAXL0NQUAb/todelete.txt
Not Found

HTTP:404

规则要点:

  • 删除令牌是一次性随机串,与下载令牌独立;X-Url-Delete 只在上传响应头出现一次,错过就要等文件过期或服务端清理;
  • 脚本里务必在拿到上传响应时同时抓取 X-Url-Delete(curl -sD - 后从头部解析),落库或落盘保存;
  • 删除请求对 URL 敏感,删除令牌段缺失会得到 404/405 而非删除成功。

七、脚本化嵌入

7.1 封装 transfer() 函数

把"打包 + 上传"封装成 shell 函数(官方 README 模式的实测等价形态),写进 ~/.bashrc 随取随用:

transfer() {
  tar -czf - "$1" | curl -s --upload-file - "http://192.168.1.172:8132/$1.tar.gz"
}

实测效果(X13,projdir 为含两个文件的目录):

transfer projdir
http://192.168.1.172:8132/PIcvKgHpX4/projdir.tar.gz

单文件版:

transferf() {
  curl -s --upload-file "$1" "http://192.168.1.172:8132/$(basename "$1")"
}

7.2 健壮性写法

  • 失败即报错:-f 让 curl 在 HTTP 4xx/5xx 时返回非零退出码,-sf 配合 || 做失败分支:
    URL=$(curl -sf --upload-file report.pdf http://192.168.1.172:8132/report.pdf) || { echo "upload failed"; exit 1; }
    
  • 同时抓下载与删除链接:用 -D - 把响应头也导出来解析:
    RESP=$(curl -sD /tmp/h.txt --upload-file report.pdf http://192.168.1.172:8132/report.pdf)
    DL_URL="$RESP"
    DEL_URL=$(grep -i '^X-Url-Delete:' /tmp/h.txt | tr -d '\r' | awk '{print $2}')
    
  • 对端校验:上传后立即回读 diff/md5(如 5.1、5.5 所示)再通知对方,避免"传完即坏了"的尴尬;
  • 限流配合:服务端若配置了 --rate-limit(行为见《transfer.sh 1.6.1 配置参数详解》5.3),高频脚本会遇到 429,重试逻辑要带上退避等待;
  • 过期参数前置校验:Max-Days 传负数会 400、传超大数会被静默忽略(4.4 实测),脚本里先做数值范围检查再发请求。

八、注意事项

  1. URL 括号要转义/引号包裹:多文件打包 URL 含 (、)、,,shell 里必须放双引号内,否则被 shell 解释成子 shell 与变量替换;
  2. stdin 上传必写保存名:管道上传没有文件名,URL 路径的文件名就是对方看到的文件名,别用默认值糊弄;
  3. X-Url-Delete 只出现一次:想要删除能力,上传命令必须带 -D -(或 -D <文件>);
  4. 删除令牌与下载令牌都是机密:拿到两者即可下载与删除,分享下载链接时不要把删除链接一并发出;
  5. 加密密码不可找回:X-Encrypt-Password/gpg 密码丢失等于文件作废(服务端存的是密文),密码要随链接一起交给接收方(建议另走安全信道);
  6. 404 的四种可能:令牌写错、文件已被删除(第六章)、Max-Days 已过期(4.4)、Max-Downloads 次数用尽(4.4)——按这个顺序排查;
  7. 413 的含义:上传超过服务端 --max-upload-size 限制,返回 Request Entity Too Large(服务端配置见 C1 5.3);
  8. wget 下载注意输出名:wget 默认用 URL 末段作为本地文件名,URL 末段就是服务端保存的文件名,通常无需 -O 改名。

九、常见问题

Q1:上传只回了一个 URL,怎么获得删除能力? 上传时加 -D -(或 -D <文件>),从响应头 X-Url-Delete 取删除链接(4.1/第六章实测)。响应体里永远只有下载 URL。

Q2:浏览器打开下载链接是"另存为",想直接预览怎么办? 把 URL 的令牌前加 /inline/(5.2)。文本、图片、PDF 都会走浏览器内置预览。

Q3:多文件打包下载报 405? URL 少了外层括号。正确形态 /(令牌1/文件1,令牌2/文件2).zip(5.4 实测:不带括号必 405)。

Q4:昨天还好的链接今天 404 了? 按 404 四种可能排查(第八章第 6 条):令牌错 → 被删除 → Max-Days 过期 → Max-Downloads 用尽。可让上传者用服务端数据目录的元数据确认(MaxDate/Downloads 字段,服务端结构见《transfer.sh 1.6.1 配置参数详解》5.2)。

Q5:上传报 413? 超过服务端 --max-upload-size(单位 KB)。联系管理员调整,或压缩/分卷后再传。

Q6:脚本高频调用时报 429? 服务端开了 --rate-limit(每分钟全站请求数上限,429 即触发限流)。给脚本加退避重试,或让管理员调大限流值(C1 5.3)。

Q7:curl 上传中途断了怎么办? PUT 上传不支持断点续传,重传即可;下载支持 Range/分片(5.3),大文件可用 curl -C - 续传。

Q8:怎么验证下载完整性? 上传前算 md5/sha256,下载后重算比对(5.5 实测形态);tar/zip 归档自带 CRC,解包报错即损坏。

十、参考链接

  • 本系列:《transfer.sh 介绍与选型指南》https://www.quzuo.net/document/nn7i2cwAAhTV、《transfer.sh 1.6.1 Docker Compose 部署指南》https://www.quzuo.net/document/k8d8smHJIjJx、《transfer.sh 1.6.1 配置参数详解》https://www.quzuo.net/document/00FWDHqIggYK
  • 官方仓库:https://github.com/dutchcoders/transfer.sh(README 含 usage 示例)
  • 版本发布页:https://github.com/dutchcoders/transfer.sh/releases/tag/v1.6.1