用ftplib实现增量上传需先调用mlsd()获取远程文件mtime并转为datetime,与本地os.path.getmtime()比较(±1秒容差),仅当本地更新或远程不存在时才storbinary()上传;降级用dir()解析;禁用被动模式可解决防火墙问题。

如何用 ftplib 判断文件是否需要上传(增量逻辑)
FTP 协议本身不支持类似 rsync 的增量校验,ftplib 也没有内置的「比对本地/远程文件修改时间或大小」功能。必须手动实现:先用 ftp.mlsd()(推荐)或 ftp.dir() 获取远程文件列表及 modify 时间戳,再与本地 os.path.getmtime() 比较。
注意:mlsd() 返回的是 FTP 服务器时区下的时间字符串(如 '20240521142345'),需统一转为 datetime 对象;若服务器不支持 MLSD 命令(老式 FTP),dir() 解析不可靠,建议换用 pyftpdlib 兼容服务端,或改用 paramiko + SFTP。
- 优先调用
ftp.mlsd(),捕获error_perm后降级到ftp.dir()+ 正则解析(仅作备用) - 本地时间用
time.time()或datetime.now().timestamp(),避免时区混淆 - 为防 FTP 时间精度丢失(部分服务器只保留到秒),建议增加 ±1 秒容差
如何安全地上传新文件并跳过已存在且未变更的文件
不要依赖 ftp.nlst() 简单判断文件是否存在——它只查文件名,无法区分内容是否一致。正确流程是:先获取远程文件元信息 → 校验 mtime 和 size → 仅当本地更新或远程不存在时才调用 ftp.storbinary()。
上传时务必使用二进制模式(storbinary),尤其对压缩包、数据库 dump 文件等非文本备份;若误用 storlines(),会导致文件损坏。
立即学习“Python免费学习笔记(深入)”;
- 上传前用
ftp.pwd()确认当前路径,避免因工作目录意外变化写错位置 - 对大文件,传入
blocksize=8192或更大值(如65536)提升吞吐,但别超过内存承受范围 - 上传后建议立即执行一次
ftp.sendcmd('MDTM ' + filename)验证远程时间是否同步(某些 FTP 服务会重置时间戳)
如何用 schedule 或系统 cron 实现可靠定时执行
Python 的 schedule 库适合开发调试,但不适用于长期后台运行(进程退出即停止)。生产环境强烈建议用系统级调度器:Linux 用 cron,Windows 用任务计划程序。
关键点在于脚本必须具备独立运行能力:显式处理异常、记录日志、避免交互等待。例如,FTP 登录失败不能卡住,要 try/except 并退出,让 cron 下次重试。
- cron 示例:
0 3 * * * /usr/bin/python3 /path/to/ftp_sync.py >> /var/log/ftp_sync.log 2>&1(每天 3 点执行) - 脚本开头加
#!/usr/bin/env python3并chmod +x,避免 cron 因 PATH 问题找不到解释器 - 所有路径(本地备份目录、日志文件)必须写绝对路径,不能依赖当前工作目录
常见报错与绕过方式:连接中断、权限拒绝、被动模式失败
ftplib 默认启用被动模式(PASV),但企业防火墙常屏蔽 PASV 的随机端口,导致 ConnectionRefusedError 或卡在 ftp.cwd()。此时应主动关闭被动模式:ftp.set_pasv(False),前提是 FTP 服务器允许主动模式且客户端出口 IP 可被反连。
其他高频错误:
-
error_perm: 550 Permission denied:检查目标目录是否可写,FTP 用户是否有STOR权限,路径是否拼错(如多出/./) -
error_temp: 425 Can't open data connection:基本是 PASV 失败,优先尝试set_pasv(False),或联系运维开放 PASV 端口段 -
timeout:设置timeout参数(如FTP(host, user, passwd, timeout=30)),避免无限等待
增量同步真正难的不是代码,而是 FTP 服务器行为的不确定性——时间戳不准、列表不全、权限策略模糊。每次上线前,务必在测试环境完整走一遍「本地改文件 → 手动删远程 → 运行脚本」闭环验证。


















