把本地的网站文件传上去、把服务器上的备份拉下来、把日志同步到另一台机器 —— 这些事天天要做,但很多人只会用 FTP 客户端拖来拖去,遇到大文件或者几万个文件时就卡住了。
命令行工具其实更好用:传输走 SSH 通道自带加密,不需要额外开端口,还能增量同步、断点续传、限速。 这篇把 scp、rsync、sftp 三个工具讲清楚,重点在 rsync —— 它是这三个里唯一值得花时间学的。
| scp | rsync | sftp | |
|---|---|---|---|
| 上手难度 | 最低 | 中 | 低 |
| 增量传输 | 不支持 | 支持 | 不支持 |
| 断点续传 | 不支持 | 支持 | 部分支持 |
| 排除规则 | 不支持 | 支持 | 不支持 |
| 限速 | -l 有限支持 | --bwlimit | -l |
| 需要两端都装 | 只要 SSH | 两端都要 rsync | 只要 SSH |
| 适合 | 偶尔传一两个文件 | 同步目录、传大量文件 | 交互式浏览和零星操作 |
一句话选择:传一两个文件用 scp;同步目录、传几万个文件、或者要断点续传用 rsync;想边看边传用 sftp。
注意 rsync 有个前提:服务器上也得装 rsync。
# 服务器上
sudo apt install -y rsync
没装的话会报 bash: rsync: command not found。scp 和 sftp 没这个问题 —— 它们只需要 SSH。
基本用法:
# 上传文件
scp local.txt [email protected]:/srv/app/
# 上传目录(-r 递归)
scp -r ./dist [email protected]:/srv/app/
# 下载
scp [email protected]:/var/log/nginx/error.log ./
# 端口是大写 P(小写 p 是保留权限,别搞混)
scp -P 2222 local.txt [email protected]:/srv/app/
# 用指定密钥
scp -i ~/.ssh/id_ed25519 local.txt [email protected]:/srv/app/
OpenSSH 9.0(2022 年)起,scp 底层已经从旧的 scp/rcp 协议改成了 SFTP 协议。
对多数人无感,但有两个实际影响:
第一,文件名里的特殊字符处理变了。 旧协议时代大家习惯对远端路径做转义(比如 scp host:"/path/with\ space.txt" .),这种写法在新协议下可能导致传输失败。文件名有空格或特殊字符时,直接加引号包住整个远端路径更稳:
scp '[email protected]:/srv/my app/config.yml' ./
第二,~user/file 这种远端路径不再被 shell 展开。 OpenSSH 8.7 加了 [email protected] 扩展来支持,但对端是不支持这个扩展的实现时就会失败。
要恢复旧行为,加 -O:
scp -O local.txt [email protected]:/srv/app/
-O 表示用 legacy scp/rcp 协议。只在遇到兼容性问题时才用 —— 新协议在安全性和文件名处理上更好。
- 传大文件:没有断点续传,网络一断就得从头来。传一个 5GB 的文件传到 90% 断了,你会很想哭
- 传几万个小文件:每个文件都要重新建立一次传输上下文,慢得离谱
- 同步目录:每次都全量传,改了 1 个文件也要传完整个目录
这三种情况都该用 rsync。
它的核心能力是增量:先比对两边的文件,只传有差异的部分,而且差异是按块算的 —— 一个大文件改了几行,只传改动的那几个块。
这是 rsync 最容易出错的地方,没有之一。
# 带斜杠:把 src 里的「内容」同步到 dst
rsync -av src/ dst/
# 不带斜杠:把 src「这个目录本身」放进 dst
rsync -av src dst/
举例说明。假设本地 src/ 里有 a.txt、b.txt:
| 命令 | 结果 |
|---|---|
rsync -av src/ dst/ | dst/a.txt、dst/b.txt |
rsync -av src dst/ | dst/src/a.txt、dst/src/b.txt |
记法:斜杠表示"进去",没斜杠表示"带上这个目录"。 部署网站时想的是前者,写错就变成多套一层目录。
rsync -avz --progress ./dist/ [email protected]:/srv/app/current/
参数拆开看:
-a=-rlptgoD,归档模式:递归、保留符号链接、权限、时间戳、属组、属主、设备文件。日常同步几乎总是要加-v输出详细信息-z传输时压缩。注意:对图片、视频、zip 这类已压缩文件没意义,还白费 CPU。纯文本(代码、日志、SQL 导出)才有收益--progress显示进度。传大文件建议加,传几万个小文件建议去掉 —— 每个文件都刷一行会拖慢速度,-v已经够了
rsync -avzP --partial-dir=.rsync-partial \
[email protected]:/backup/db-2026-09-28.sql.gz ./
-P等于--partial --progress,是断点续传最常用的组合--partial让中断时保留已传的部分,下次接着传--partial-dir把临时文件放到指定子目录,比直接在目标目录留半截文件更干净
更严谨的做法是加 --append-verify:它假定目标端已有的部分是正确的前缀,只追加剩余部分,并在结束后校验整体。适用于"确认前面传对了、只是被中断"的场景。如果文件内容在中断期间变过,不要用它 —— 会拼出一个坏文件。
rsync -avz \
--exclude='.git' \
--exclude='node_modules' \
--exclude='*.log' \
--exclude='cache/' \
./project/ [email protected]:/srv/app/
规则多了就写进文件:
# .rsyncignore
.git/
node_modules/
*.log
cache/
.env
rsync -avz --exclude-from='.rsyncignore' ./project/ [email protected]:/srv/app/
排除 .env 很重要 —— 生产环境的配置文件不该被本地版本覆盖。同理要排除 .git,否则每次同步都要传一大堆对象文件。
rsync -avz --bwlimit=2000 ./backup/ [email protected]:/backup/
--bwlimit 的单位是 KB/s,所以 2000 约等于 2MB/s。传大备份时加上,避免把带宽占满导致网站变慢 —— 尤其是按流量计费或者带宽很小的机器。流量监控的做法见 VPS 流量监控和超额预警。
--delete 会让目标端删掉源端没有的文件,让两边完全一致。同步镜像站时很有用,但用错了就是删库。
养成习惯:加 --delete 之前先跑一次 --dry-run(或 -n)。
# 先演练,只打印会做什么,不实际执行
rsync -avz --delete --dry-run ./dist/ [email protected]:/srv/app/current/
# 确认输出符合预期,再去掉 --dry-run
rsync -avz --delete ./dist/ [email protected]:/srv/app/current/
特别注意源端写错的情况:如果本地目录因为笔误是空的,--delete 会把服务器上的文件全删光。--dry-run 那一遍就是防这个的。
想更保守可以用 --delete-after(先传完再删,而不是边传边删),或者 --max-delete=100 给删除数量设个上限。
这是个高频混淆点:scp 用 -P(大写)指定端口,但 rsync 没有 -P 端口参数(rsync 的 -P 是断点续传)。要用 -e 传 SSH 参数:
rsync -avz -e "ssh -p 2222" ./dist/ [email protected]:/srv/app/
指定密钥也一样:
rsync -avz -e "ssh -p 2222 -i ~/.ssh/id_ed25519" ./dist/ [email protected]:/srv/app/
参数多了就写进 ~/.ssh/config(见 SSH 密钥登录配置),然后 rsync 直接写主机别名:
rsync -avz ./dist/ myserver:/srv/app/
-a 会保留本地权限,但传上去之后文件属主往往不对 —— 本地是 you:you,服务器上应该是 www-data 或专门的部署用户。
rsync -avz --chown=www-data:www-data ./dist/ [email protected]:/srv/app/
--chown 在目标端生效,需要远端 rsync 有相应权限(root 或 sudo)。
如果同步后网站报 403,先检查文件属主和权限,排查思路见 VPS 网站出现 403 Forbidden 怎么办。
适合"上去看看、顺手传两个文件":
sftp [email protected]
# 进去之后
ls # 列远端目录
lls # 列本地目录(前面加 l 表示 local)
cd /srv/app # 切换远端目录
lcd ~/Downloads # 切换本地目录
get backup.sql.gz # 下载
put config.yml # 上传
get -r logs/ # 递归下载目录
mkdir newdir # 建目录
rm oldfile # 删文件
bye # 退出
sftp 也支持续传(比 scp 强的地方):
reget backup.sql.gz # 续传下载
reput bigfile.iso # 续传上传
指定端口和其他参数跟 ssh 一样:sftp -P 2222 [email protected](注意 sftp 用的是大写 -P)。
一次性执行(不进交互界面):
sftp [email protected] <<'EOF'
cd /srv/app
put config.yml
bye
EOF
安全提醒:sftp 只应该允许访问该用户自己的目录。如果要给外部人员开 sftp,务必限制在指定目录内(sshd_config 里配 ChrootDirectory),否则对方能看到整台服务器的文件。
命令行不顺手的话,这几个图形客户端都能用密钥连接:
| 工具 | 平台 | 说明 |
|---|---|---|
| WinSCP | Windows | 老牌,支持同步、脚本化 |
| FileZilla | 全平台 | 支持 SFTP,界面直观 |
| Termius / MobaXterm | 全平台 | 终端 + 文件管理一体 |
| Cyberduck | macOS / Windows | 也能连对象存储 |
用之前注意两件事:
- 别用 FTP,用 SFTP。 普通 FTP 是明文的,密码和文件内容都能被截获。这些客户端都支持 SFTP,选它。
- 用密钥而不是密码。 配置方式和 SSH 一样,见 SSH 密钥登录配置。
图形工具的短板是慢:传几万个小文件时,界面刷新本身就要很久,而且不能像 rsync 那样配排除规则。批量同步还是老老实实用 rsync。
传几个 G 的文件时,速度往往远低于带宽上限。原因通常是这几个:
1. 单条 TCP 连接受带宽时延积限制。 跨国线路延迟 200ms、单流 TCP 窗口不够大时,即使带宽充足也跑不快。加大窗口有帮助,但更有效的是分片并行传输。
2. 加密开销。 小 CPU 的机器上,加密可能成为瓶颈。可以换更快的算法:
rsync -avz -e "ssh -c [email protected]" ./bigfile [email protected]:/data/
[email protected] 在现代 CPU 上有硬件加速,比默认算法轻。但这只在小机器上才有明显效果。
3. 磁盘 IO 是瓶颈。 一边读一边写,如果磁盘本来就慢(比如大盘鸡的机械盘),传输速度会被它拖住。看看 iostat 的 %util,排查方法见 VPS 磁盘 I/O 很高怎么办。
4. 别对已压缩文件加 -z。 图片、视频、zip、gz 本身已经压缩过,再压一遍纯属浪费 CPU。
实操建议:
- 大文件用
rsync -P,中断能接着传,不用重来 - 传之前先
gzip文本类文件(SQL 导出、日志),能省一半以上体积 - 体积特别大又只在两处之间搬,考虑先传到对象存储再拉,有时候比直连快,也顺便有了异地备份,见 VPS 需要对象存储吗
- 传完记得
md5sum或sha256sum校验两端一致,别默认"传完就是对的"
部署网站(本地构建 → 服务器)
rsync -avz --delete --dry-run ./dist/ [email protected]:/srv/app/current/
# 确认无误后去掉 --dry-run
rsync -avz --delete ./dist/ [email protected]:/srv/app/current/
拉取服务器备份到本地
rsync -avzP --bwlimit=5000 \
[email protected]:/srv/backups/ ~/vps-backups/
把旧服务器的数据同步到新服务器(服务器间直传)
# 在旧服务器上执行,直接推到新机器
rsync -avzP /srv/app/ root@新IP:/srv/app/
比"下载到本地再上传"快得多,因为走的是两台服务器之间的线路。整体迁移流程见 VPS 迁移指南。
同步日志用于分析
rsync -avz --include='*.log' --exclude='*' \
[email protected]:/var/log/nginx/ ./logs/
只同步最近 7 天的文件
rsync -avz --files-from=<(find /srv/app -type f -mtime -7 -printf '%P\n') \
/srv/app/ [email protected]:/srv/app/
rsync: command not found
服务器上没装 rsync(本地装了没用,两端都要)。sudo apt install -y rsync。
Permission denied (publickey)
SSH 认证问题,和传输本身无关。见 SSH Permission denied 怎么办。
rsync: failed to set times on ...: Operation not permitted
没有保留时间戳的权限。目标端不是 root 时会这样。可以加 --no-times,或者用有权限的账号。
传完文件属主不对、网站报 403
加 --chown,或者传完在服务器上 chown -R。见 403 Forbidden 排查。
No space left on device
目标分区满了。先看是哪满了:
df -h # 空间
df -i # inode(小文件特别多时会先耗尽这个)
清理方法见 VPS 硬盘满了怎么办 和 磁盘还有空间却写不了文件。
传输中途断开
网络抖动或被服务端超时清理。rsync 加 -P 续传;连接本身的稳定性问题见 SSH 总是断开怎么办。
速度只有几十 KB/s
先确认不是被限速(--bwlimit 忘了去掉)、不是磁盘 IO 打满、也不是被服务商限速。排查思路见 VPS 被限速了怎么办。
传输过程加密吗?
加密。三个工具都走 SSH 通道,和普通 SSH 会话同等强度。所以不需要额外配 TLS,也不该用明文 FTP。
能传符号链接吗?
-a 会保留符号链接本身。如果你想让 rsync 跟随链接传实际内容,加 -L。注意 -L 可能造成循环引用,目录有软链回上层时要小心。
Windows 上怎么用 rsync?
Windows 没有原生 rsync。三个选择:用 WSL(最省事,wsl rsync ...)、装 cwRsync、或者用 WinSCP 的同步功能。scp 和 sftp 则可以直接用 —— Win10 1809+ 自带 OpenSSH 客户端。
怎么确认传过去的文件是完整的?
传完在两端各算一次哈希:
# 本地
sha256sum bigfile.tar.gz
# 服务器
sha256sum /srv/bigfile.tar.gz
rsync 自己也有校验机制(默认用文件大小+修改时间判断,-c 可以改为按内容校验,但会慢很多)。
能不能两台服务器之间直传,不经过我的电脑?
能,而且强烈建议这么做。在任意一台上执行 rsync,目标写另一台:
rsync -avzP /data/ root@另一台IP:/data/
前提是这台服务器能免密 SSH 到那台(需要配好密钥)。走服务器之间的线路,通常比绕经家宽快得多。
定时自动同步怎么做?
写成脚本,用 systemd timer 或 cron 跑。注意脚本里要写全路径、配好密钥免密,否则非交互环境下会卡在密码提示。定时任务的排查见 VPS 定时任务不执行怎么办。
同步和备份是一回事吗?
不是。rsync --delete 是同步,不是备份 —— 本地误删了,同步之后服务器上的也没了。真正的备份要保留多个时间点、存在不同地方、并且演练过恢复,做法见 VPS 备份恢复演练。
- VPS 配置 SSH 密钥登录 — 免密是自动化传输的前提
- VPS 备份恢复演练怎么做 — 同步不等于备份
- VPS 迁移指南 — 服务器之间批量搬数据
- VPS 硬盘满了怎么办 — 传完发现写不进去
- VPS 上 SSH 端口转发与隧道 — 传输之外,SSH 还能做这些
