ftplib本身不支持定时,需结合schedule或cron;默认主动模式不适用公网,须显式set_pasv(True);无重试、无连接复用、无SSL自动协商,需手动补足超时、FTP_TLS加密、文件校验等生产级能力。

FTPlib 不适合做自动化定时上传下载——它本身不提供定时能力,且默认不处理被动模式、SSL、连接复用等生产环境必需特性。真正要落地,得靠组合方案:用 ftplib 做协议交互,用外部调度(如 schedule 或系统 cron)控制时机,并手动补足健壮性短板。
为什么直接用 ftplib 写定时脚本容易失败
常见现象是脚本本地能跑通,一放到服务器上就卡在 connect() 或 retrbinary(),甚至上传一半断连不报错。根本原因不是代码写错了,而是 ftplib 默认行为和真实 FTP 服务差异太大:
-
FTP类默认主动模式(PORT),但绝大多数公网 FTP 服务器禁用该模式,必须显式切换到被动模式:ftp.set_pasv(True) - 没有内置重试机制,网络抖动或临时 5xx 响应会导致整个任务中断
- 连接不自动复用,每次操作都新建 TCP 连接,频繁调用会触发服务器限流(尤其共享主机)
- 明文传输账号密码,若服务器要求
FTPES(显式 TLS),ftplib.FTP会直接报534 Policy requires SSL
用 ftplib.FTP_TLS 安全连接并保持会话
如果目标服务器启用 TLS(越来越常见),必须用 FTP_TLS 替代 FTP,且需在登录后立即调用 auth() 和 prot_p() 启用加密通道:
from ftplib import FTP_TLS
import ssl
<p>ftp = FTP_TLS()
ftp.connect('ftp.example.com', 21)
ftp.auth() # 发送 AUTH TLS 命令
ftp.prot_p() # 切换数据连接为加密
ftp.login('user', 'pass')</p><h1>后续 retrbinary / storbinary 都走加密通道</h1><p>with open('local.txt', 'rb') as f:
ftp.storbinary('STOR remote.txt', f)</p><p>ftp.quit()
注意:FTP_TLS 默认不校验证书,生产环境应传入自定义 ssl_context 防中间人攻击;若服务器证书是自签名,需设 context.check_hostname = False,但仅限内网可信环境。
立即学习“Python免费学习笔记(深入)”;
用 schedule 库实现轻量定时,而非轮询
别用 time.sleep(3600) 死循环——进程挂了就停,日志难追踪,也不支持按周/月等复杂周期。用 schedule 更可靠:
- 安装:
pip install schedule - 每小时执行一次上传:
schedule.every().hour.do(upload_file, ftp_host='x', user='y') - 每天凌晨 2 点下载:
schedule.every().day.at("02:00").do(download_backup) - 主循环里加异常捕获,避免单次失败阻塞后续调度:
schedule.run_pending(); time.sleep(60)
真正上线时,建议把调度逻辑拆进独立进程(如用 systemd 或 supervisord 管理),而不是丢进 crontab 跑 Python 脚本——前者能自动拉起崩溃进程,后者每次都是新进程,状态无法延续。
文件传输必须加超时和回调,否则不知道卡在哪
ftplib 的 storbinary() 和 retrbinary() 默认无超时,遇到慢速网络或大文件可能挂住几小时。必须显式传参:
- 连接超时:
FTP(timeout=30)或FTP_TLS(timeout=30) - 传输超时:在
storbinary()中用callback函数分块校验,例如每写入 1MB 打印进度或更新时间戳 - 关键检查点:上传后务必调用
ftp.size('remote.txt')对比本地os.path.getsize(),防止因 FTP 服务器磁盘满导致静默截断
最容易被忽略的是被动模式下数据连接的防火墙穿透问题——如果你的脚本部署在 Docker 容器或 NAT 后,set_pasv(True) 返回的 IP 往往是服务器内网地址,客户端连不上。此时要么改用主动模式(需开放高位端口并配置 ftp.sendcmd('PORT ...')),要么换更现代的库如 pyftpdlib 或直接上 paramiko(SFTP)。


















