4. FTP固件下载
任务目标
实现一个基于FTP协议的固件下载客户端,支持用户名/密码认证与被动模式(PASV),将远程固件文件完整下载到本地指定路径,并校验文件大小以确保下载完整性。
环境依赖
本案例使用Python 3.8+标准库 ftplib 实现,无需安装第三方包。运行环境需具备网络访问FTP服务器的能力。
核心实现
import ftplib
import os
from typing import Tuple
class FTPFirmwareDownloader:
def __init__(self, host: str, port: int = 21, timeout: int = 30):
self.host = host
self.port = port
self.timeout = timeout
self.ftp = None
def connect(self, username: str, password: str) -> None:
"""建立FTP连接并完成用户认证"""
self.ftp = ftplib.FTP()
self.ftp.connect(self.host, self.port, self.timeout)
self.ftp.login(username, password)
# 强制切换到被动模式(PASV)
self.ftp.set_pasv(True)
def download_firmware(self, remote_path: str, local_path: str) -> Tuple[bool, str]:
"""
下载固件文件到本地
返回: (是否成功, 结果描述)
"""
if not self.ftp:
return False, "未建立FTP连接,请先调用connect()"
try:
# 获取远程文件大小(用于校验)
remote_size = self.ftp.size(remote_path)
if remote_size is None:
return False, "无法获取远程文件大小,文件可能不存在"
# 执行二进制下载
with open(local_path, 'wb') as fp:
self.ftp.retrbinary(f'RETR {remote_path}', fp.write)
# 校验本地文件大小
local_size = os.path.getsize(local_path)
if local_size != remote_size:
os.remove(local_path) # 删除不完整文件
return False, f"文件大小不匹配: 远程{remote_size}字节, 本地{local_size}字节"
return True, f"下载成功: {local_path} ({local_size}字节)"
except ftplib.all_errors as e:
# 清理可能残留的本地文件
if os.path.exists(local_path):
os.remove(local_path)
return False, f"FTP错误: {str(e)}"
except OSError as e:
return False, f"本地文件错误: {str(e)}"
def close(self) -> None:
"""关闭FTP连接"""
if self.ftp:
self.ftp.quit()
self.ftp = None
if __name__ == "__main__":
# 使用示例(请替换为实际FTP服务器参数)
FTP_HOST = "192.168.1.100"
FTP_PORT = 21
FTP_USER = "firmware_user"
FTP_PASS = "secure_pass"
REMOTE_FILE = "/firmware/esp32_app_v2.1.bin"
LOCAL_FILE = "./esp32_app_v2.1.bin"
downloader = FTPFirmwareDownloader(FTP_HOST, FTP_PORT)
try:
downloader.connect(FTP_USER, FTP_PASS)
success, message = downloader.download_firmware(REMOTE_FILE, LOCAL_FILE)
print(message)
if not success:
exit(1)
finally:
downloader.close()
运行与验证
运行方式:将上述代码保存为 ftp_download.py,修改 __main__ 中的FTP服务器地址、端口、用户名、密码及远程/本地文件路径后执行:
python3 ftp_download.py
验证方法:
- 正常路径:程序输出
下载成功: 本地路径 (N字节),且本地文件大小与FTP服务器上文件大小一致。 - 认证失败:使用错误密码时,程序应抛出
ftplib.error_perm异常并输出错误信息,退出码为1。 - 文件不存在:远程路径错误时,输出
无法获取远程文件大小或FTP错误信息。 - 被动模式确认:可在FTP服务器端日志中观察到客户端使用PASV命令建立数据连接。
关键说明
- 代码通过
set_pasv(True)显式启用被动模式,这是NAT/防火墙环境下最常用的FTP传输方式。 - 下载完成后自动比对远程与本地文件大小,防止因网络中断导致固件不完整。
- 若需支持断点续传或进度显示,可在
retrbinary回调函数中扩展,但当前最小实现已覆盖FTP固件下载的核心闭环。