30个网站的FTP文件手动下载备份,每个月做一次,一次3-4小时。换成FreeFileSync做增量镜像,第一次全量备份花40分钟,之后每次只同步变更文件,3分钟跑完。再用lftp的mirror命令写成脚本挂到crontab上,每天凌晨自动把30个站的最新文件镜像到本地NAS,人工成本直接降到零。同样是做FTP批量备份,不同方案的效率差了60倍。
五种FTP批量备份方案速查
| 🖥️ 图形同步工具:FreeFileSync / GoodSync / Syncthing | 📜 命令行镜像:lftp mirror / wget递归下载 |
| 🐍 Python脚本:ftplib批量下载 + 增量判断 + 压缩打包 | 🔧 FTP客户端脚本:WinSCP脚本 + Windows计划任务 |
| 🔄 服务器端自动推送:rsync + crontab 定时打包推送到备份服务器 | |
一、FTP备份的三种模式:全量、增量、镜像,差别很大
在做FTP批量备份之前,先想清楚你需要哪种模式。模式选错了,要么备份太慢,要么磁盘被撑爆。
如果你只是想"网站出问题了能恢复",增量备份最实用——第一次全量拉下来,之后每天只拉变更的文件。镜像备份比增量多一步:如果服务器上删了某个文件,镜像备份也会在本地删掉它。这对于"本地是服务器的精确副本"这个需求很有用,但如果你需要保留历史版本(比如恢复三天前的某个被误删的文件),镜像备份就不合适了。
二、FreeFileSync:零门槛的图形化批量备份方案
FreeFileSync是目前最推荐的开源FTP备份工具。它通过图形界面配置同步规则,支持FTP/SFTP/FTPS协议,能自动对比本地和远程的文件差异,只传输有变化的文件。
配置一次,之后每次点"同步"就行:

- 下载安装FreeFileSync(freefilesync.org,Windows/Mac/Linux全支持)
- 左侧选本地备份目录,右侧点"云"图标→选FTP→输入服务器IP、用户名、密码、端口
- 点"比较"→软件自动扫描两边文件差异,绿色=新增、蓝色=修改、灰色=未变化
- 点"同步"→选择模式(镜像/更新/双向),确认后开始传输
- 保存配置为.ffs_batch文件,之后双击这个文件自动执行同步
批量备份30个站点的正确做法:不要30个站配30个同步任务一个一个点。在FreeFileSync里,每个站点创建一个同步配置,保存为独立的.ffs_batch文件。然后写一个简单的批处理脚本把所有配置文件串起来:
@echo offREM backup_all.bat —— 批量备份所有站点set FFS="C:\Program Files\FreeFileSync\FreeFileSync.exe"echo [%time%] 开始备份站点1...%FFS% "D:\backup_configs\site01.ffs_batch"echo [%time%] 开始备份站点2...%FFS% "D:\backup_configs\site02.ffs_batch"REM ... 依次到site30echo [%time%] 全部备份完成!
把这个bat文件加到Windows计划任务里,设置每天凌晨3点执行,30个站的FTP增量备份就全自动了。
FreeFileSync的三个同步模式怎么选:镜像模式(Mirror)= 本地完全等于远程,远程删了本地也删。更新模式(Update)= 只把远程的新文件和修改过的文件复制到本地,远程删了本地保留。双向模式(Two-way)= 两边互相同步,哪边新用哪边。FTP备份通常用更新模式最安全——万一服务器被人删了文件,你本地至少还留着。
三、lftp mirror:Linux下最快、最稳的命令行FTP备份
如果你的备份机是Linux,lftp的mirror命令是效率最高的方案。它支持多线程并发传输、断点续传、增量对比,而且完全免费。

# 安装lftpapt install lftp # Debian/Ubuntuyum install lftp # CentOS/Rocky# 单个站点镜像备份(增量,只下载新文件和修改过的文件)lftp -u username,password ftp.example.com << EOFset ftp:ssl-allow nomirror --verbose --only-newer --parallel=5 /remote/path /local/backup/pathquitEOF# 关键参数说明:# --only-newer: 只下载比本地新的文件(增量备份核心)# --parallel=5: 5个文件并发下载# --delete: 加这个参数变成镜像模式(远程删了本地也删)# --exclude: 排除不需要备份的目录,如缓存
批量备份30个站的完整脚本:
#!/bin/bash# backup_all_sites.sh —— 批量FTP备份30个站点SITES=("site01|ftp1.example.com|user1|pass1|/www/site01|/backup/site01""site02|ftp2.example.com|user2|pass2|/www/site02|/backup/site02"# ... 依次到 site30)LOG_FILE="/var/log/ftp_backup_$(date +%Y%m%d).log"for site in "${SITES[@]}"; doIFS='|' read -r name host user pass remote local <<< "$site"echo "[$(date)] 开始备份 $name ..." >> "$LOG_FILE"lftp -u "$user","$pass" "$host" << EOF >> "$LOG_FILE" 2>&1set ftp:ssl-allow nomirror --verbose --only-newer --parallel=3 \--exclude cache/ --exclude tmp/ --exclude .git/ \"$remote" "$local"quitEOFif [ $? -eq 0 ]; thenecho "[$(date)] $name 备份成功" >> "$LOG_FILE"elseecho "[$(date)] $name 备份失败!" >> "$LOG_FILE"fidoneecho "[$(date)] 全部备份任务完成" >> "$LOG_FILE"
把这个脚本加到crontab:0 3 * * * /root/backup_all_sites.sh,每天凌晨3点自动执行。
四、Python ftplib脚本:可以加自定义逻辑的批量备份
如果你需要在备份过程中加入自定义逻辑——比如备份前先压缩、备份后发邮件通知、只备份最近7天修改过的文件、按站点名称自动创建日期目录——用Python脚本比lftp灵活得多。
import osimport ftplibfrom datetime import datetimefrom concurrent.futures import ThreadPoolExecutorSITES = [{'name':'site01', 'host':'ftp1.example.com','user':'user1', 'pass':'pass1', 'remote':'/www/site01'},# ... 依次到site30]BACKUP_ROOT = '/backup'date_str = datetime.now().strftime('%Y%m%d')def download_file(ftp, remote_path, local_path):"""下载单个文件,自动创建目录"""os.makedirs(os.path.dirname(local_path), exist_ok=True)with open(local_path, 'wb') as f:ftp.retrbinary(f'RETR {remote_path}', f.write)def mirror_dir(ftp, remote_dir, local_dir):"""递归镜像整个目录,跳过已存在且未修改的文件"""try:ftp.cwd(remote_dir)except:returnos.makedirs(local_dir, exist_ok=True)items = []ftp.retrlines('MLSD', items.append)for line in items:parts = line.split(';')name = parts[-1].strip().split(' ', 1)[1]is_dir = 'type=dir' in lineremote_path = f'{remote_dir}/{name}'local_path = f'{local_dir}/{name}'if is_dir:if name not in ['.','..','cache','tmp']:mirror_dir(ftp, remote_path, local_path)else:# 增量判断:本地已有且大小一致就跳过if os.path.exists(local_path):remote_size = ftp.size(remote_path)local_size = os.path.getsize(local_path)if remote_size == local_size:continuedownload_file(ftp, remote_path, local_path)def backup_site(site):"""备份单个站点"""try:ftp = ftplib.FTP(site['host'])ftp.login(site['user'], site['pass'])local_dir = f'{BACKUP_ROOT}/{site["name"]}/{date_str}'mirror_dir(ftp, site['remote'], local_dir)ftp.quit()print(f"✅ {site['name']} 备份完成")return Trueexcept Exception as e:print(f"❌ {site['name']} 备份失败: {e}")return False# 用线程池并发备份多个站点with ThreadPoolExecutor(max_workers=5) as executor:results = list(executor.map(backup_site, SITES))success = sum(results)print(f"\n完成!成功 {success}/{len(SITES)} 个站点")
这个Python脚本的核心逻辑:用MLSD命令列出FTP目录→递归遍历→对比本地文件大小→大小一致就跳过→不一致或本地没有就下载。比lftp的mirror命令慢一些(因为MLSD逐文件对比比lftp的批量对比效率低),但好处是你可以在这个框架上加任何自定义逻辑——比如备份完成后自动打包成tar.gz、自动上传到云存储、自动发钉钉/企业微信通知。

五、WinSCP脚本 + Windows计划任务:纯Windows环境下的自动化
如果你用的是Windows电脑做备份机,不想装FreeFileSync,也不想学Linux命令,WinSCP自带的脚本功能是最简单的自动化方案。
# backup_site01.txt —— WinSCP脚本文件option batch onoption confirm offopen ftp://user1:pass1@ftp1.example.comsynchronize local -mirror D:\backup\site01 /www/site01closeexit
REM backup_all.bat —— 调用WinSCP执行所有备份脚本set WINSCP="C:\Program Files (x86)\WinSCP\WinSCP.com"%WINSCP% /script=D:\scripts\backup_site01.txt /log=D:\logs\site01.log%WINSCP% /script=D:\scripts\backup_site02.txt /log=D:\logs\site02.logREM ... 依次到site30
WinSCP的synchronize命令自带增量对比功能(跟FreeFileSync类似),只传输有变化的文件。把这个bat加到Windows任务计划程序里设置每天定时执行,就能实现全自动的FTP批量备份。
六、五种方案怎么选
七、FTP批量备份的四个坑
1. 用ASCII模式传二进制文件导致损坏。FTP有两种传输模式:ASCII(文本)和Binary(二进制)。图片、压缩包、字体文件必须用Binary模式传。如果图片文件用ASCII模式传输,文件大小可能对但内容已损坏,打开显示"图片无法加载"。lftp默认自动判断,Python ftplib默认是ASCII需要手动设置,FreeFileSync和WinSCP默认Binary不用管。
2. 备份时文件正在被写入。如果备份脚本运行时网站正好在上传文件或写入日志,可能备份到一个"写了一半"的损坏文件。解决办法:备份时间设在凌晨访问低谷期,或者先对网站做快照(如果服务器支持),再备份快照。
3. 密码明文写在脚本里。lftp脚本和Python脚本里直接写了FTP密码,如果脚本文件被别人看到密码就泄露了。生产环境建议把密码存在环境变量或单独的配置文件里,并设置文件权限为600(仅root可读)。
4. 只备份了文件没备份数据库。FTP备份只能备份网站文件(HTML/PHP/图片等),但动态网站的内容存在数据库(MySQL/PostgreSQL)里。数据库不在FTP目录下,需要单独用mysqldump或pg_dump导出。完整的网站备份 = FTP文件备份 + 数据库导出,两者缺一不可。
FTP批量备份这件事,如果你的站点不超过10个,FreeFileSync点几下鼠标配完就能用,每周手动跑一次也不费事。超过20个站点或者需要每天备份,直接上lftp脚本+ crontab,配置一次之后永远不用管。关键不是选哪个工具,关键是先把备份做起来——没备份的网站,出问题那一刻的损失远大于花一个下午搭备份系统的时间。
