Python ftplib上传超时且无法续传问题求助
解决不稳定FTP服务器大文件上传超时且无法续传的问题
嘿,我来帮你搞定这个FTP续传失效的麻烦!我之前在处理不稳定服务器的大文件上传时也踩过类似的坑,你的代码思路是对的,但几个关键细节没处理到位,导致超时后没法续传。咱们一步步来排查和修复:
核心问题分析
你的代码里已经有了进度跟踪器,但缺少以下几个关键环节:
- 超时机制缺失:默认的
ftplib超时设置太短,或者没有处理传输过程中的socket超时异常 - 续传校验逻辑不完善:没有准确获取服务器上已上传的文件大小,本地文件指针也没正确定位到续传位置
- 被动模式未启用:不稳定网络下,主动模式(PORT)容易被防火墙拦截,被动模式(PASV)更可靠
- 异常重试机制不足:遇到超时、连接重置等异常时,直接退出而不是尝试重新连接续传
修复后的完整代码示例
#!/usr/bin/env python3 import ftplib import os import sys import time import socket from configparser import ConfigParser class FtpUploadTracker: def __init__(self, total_size, uploaded_size=0): self.size_written = uploaded_size self.total_size = total_size self.last_shown_percent = 0 def handle(self, block): self.size_written += len(block) # 这里别用固定1024,block大小可能不固定 percent_complete = round((self.size_written / self.total_size) * 100, 1) if percent_complete > self.last_shown_percent: print(f"上传进度: {percent_complete}% ({self.size_written}/{self.total_size} bytes)", end='\r') self.last_shown_percent = percent_complete def upload_file_with_resume(ftp_host, ftp_user, ftp_pass, local_file_path, remote_file_path, max_retries=5, timeout=300): local_file_size = os.path.getsize(local_file_path) uploaded_size = 0 for retry in range(max_retries): try: # 初始化FTP连接,设置超时 ftp = ftplib.FTP(ftp_host, timeout=timeout) ftp.login(ftp_user, ftp_pass) ftp.set_pasv(True) # 启用被动模式,适配不稳定网络 # 检查服务器上是否已有该文件,获取已上传大小 try: remote_file_size = ftp.size(remote_file_path) # 校验本地和远程文件大小,避免续传错误 if remote_file_size < local_file_size: uploaded_size = remote_file_size print(f"检测到已上传 {uploaded_size} bytes,开始续传...") else: print("远程文件已完整,无需上传") return except ftplib.error_perm: # 文件不存在,从头开始上传 uploaded_size = 0 print("远程文件不存在,开始全新上传...") # 打开本地文件,定位到续传位置 with open(local_file_path, 'rb') as f: f.seek(uploaded_size) tracker = FtpUploadTracker(local_file_size, uploaded_size) # 使用STOR命令续传,注意要加REST参数指定起始位置 ftp.storbinary(f"STOR {remote_file_path}", f, blocksize=1024*1024, callback=tracker.handle, rest=uploaded_size) print("\n上传完成!") ftp.quit() return except (socket.timeout, ftplib.error_temp, ConnectionResetError) as e: print(f"\n上传出错: {str(e)},第 {retry+1}/{max_retries} 次重试...") time.sleep(5) # 重试前等待5秒 continue except Exception as e: print(f"\n未知错误: {str(e)},终止上传") raise print(f"\n已重试 {max_retries} 次仍失败,请检查服务器连接或文件") if __name__ == "__main__": # 这里可以替换成你的配置,或者从ConfigParser读取 config = { 'ftp_host': 'your-ftp-host', 'ftp_user': 'your-ftp-user', 'ftp_pass': 'your-ftp-pass', 'local_file': '/path/to/your/large-file', 'remote_file': '/remote/path/to/large-file' } upload_file_with_resume( config['ftp_host'], config['ftp_user'], config['ftp_pass'], config['local_file'], config['remote_file'] )
关键修复点解释
- 完善进度跟踪器:用
len(block)替代固定的1024,因为storbinary的block大小可能因网络情况变化,避免进度计算错误 - 超时与被动模式:初始化FTP时设置全局超时,同时启用被动模式
set_pasv(True),解决不稳定网络下的连接问题 - 续传校验逻辑:通过
ftp.size()获取远程文件大小,只有当远程文件小于本地时才续传,避免覆盖已完成的文件 - 异常重试机制:捕获
socket.timeout、ConnectionResetError等常见网络异常,最多重试5次,每次重试前等待几秒 - 正确的续传命令:使用
rest=uploaded_size参数告诉FTP服务器从指定位置开始接收数据,这是断点续传的核心
额外建议
- 如果服务器支持,可以开启FTP的SSL/TLS加密(使用
ftplib.FTP_TLS),提升传输稳定性和安全性 - 对于超大文件(比如10GB以上),可以考虑分块上传后在服务器合并,但需要服务器支持文件操作命令
- 可以增加日志记录,把每次重试的时间、错误信息保存到文件,方便后续排查问题
内容的提问来源于stack exchange,提问作者GuySoft
相关产品推荐
相关产品推荐

