You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何解决Python3中ftplib批量上传文件时的FTP超时问题?

解决ftplib上传大量文件时的连接超时问题

看到你用ftplib处理上千个文件上传时遇到连接超时的问题,这在批量FTP操作里太常见了——你的现有代码其实有几个可以优化的点,我来一步步帮你调整:

先分析现有代码的问题

你的代码里,一旦某个文件上传出错就直接ftp.quit()+ftp.close(),后续文件没有重新建立连接肯定会报错;另外默认的FTP连接超时可能太短,长时间闲置或者大文件上传时容易触发超时;还有没有重试机制,偶然的网络波动就会导致上传失败。

优化方案和修改后的代码

我会给代码加上超时设置、重试机制、连接保活这几个关键功能,让批量上传更稳定:

import ftplib
import os
import time

def uploader(self, chunk):
    print("ON UPLOADER", chunk)
    print("Uploader", self.outer)
    pnglist = []
    csvlist = []
    print("Changed")
    
    # 先处理本地目录切换
    try:
        base_dir = '/home/ubuntu/rabit'
        self.outer = os.path.join(base_dir, self.outputdir)
        os.chdir(self.outer)
    except Exception as e:
        print("Directory error:", self.outer)
        print("Exception:", e)
        return 1  # 目录有问题直接返回,不用继续
    
    # 定义一个重新建立FTP连接的函数
    def get_ftp_connection():
        ftp = ftplib.FTP(self.host, timeout=300)  # 设置5分钟超时,根据你的需求调整
        ftp.login(self.username, self.password)
        ftp.cwd(self.outputdir)
        return ftp
    
    ftp = get_ftp_connection()
    retry_count = 3  # 每个文件最多重试3次
    files_processed = 0
    keep_alive_interval = 50  # 每上传50个文件发送一次保活命令
    
    for file1 in chunk:
        print("Processing file:", file1)
        success = False
        current_retry = 0
        
        while current_retry < retry_count and not success:
            try:
                # 定期发送NOOP保持连接活跃
                if files_processed % keep_alive_interval == 0 and files_processed > 0:
                    ftp.voidcmd('NOOP')
                    print("Sent NOOP to keep connection alive")
                
                with open(file1, 'rb') as f:
                    ftp.storbinary('STOR ' + file1, f)
                # os.remove(file1)  # 建议确认上传成功后再删除,避免误删
                print(f"{file1} uploaded successfully")
                success = True
                files_processed += 1
                
                # 数据库标记成功
                SingletonDecorator(
                    f"UPDATE gdl_input_files SET processed ='4' , errordata='0' WHERE filename='{file1}'", 
                    function="insertion"
                )
                
            except (ftplib.error_temp, ftplib.error_perm, ConnectionResetError, TimeoutError) as e:
                current_retry += 1
                print(f"Error uploading {file1}, retry {current_retry}/{retry_count}: {e}")
                # 出错后尝试重新连接
                if current_retry < retry_count:
                    try:
                        ftp.quit()
                    except:
                        pass  # 可能连接已经断开,忽略quit错误
                    ftp = get_ftp_connection()
                    time.sleep(2)  # 重试前等待2秒,避免频繁连接
                
            except Exception as e:
                print(f"Unexpected error with {file1}: {e}")
                # 非超时/连接类错误,直接标记失败
                SingletonDecorator(
                    f"UPDATE gdl_input_files SET processed ='4' , errordata='1' WHERE filename='{file1}'", 
                    function="insertion"
                )
                break
        
        # 如果重试多次还是失败,标记错误
        if not success:
            print(f"Failed to upload {file1} after {retry_count} retries")
            SingletonDecorator(
                f"UPDATE gdl_input_files SET processed ='4' , errordata='1' WHERE filename='{file1}'", 
                function="insertion"
            )
    
    # 最后关闭连接
    try:
        ftp.quit()
    except:
        ftp.close()
    
    return 0

关键优化点说明

  • 设置连接超时:初始化FTP时指定timeout=300(5分钟),避免默认的短超时导致闲置断开。
  • 连接保活:每上传50个文件发送NOOP命令,告诉服务器连接还在活跃,防止被主动断开。
  • 重试机制:每个文件最多重试3次,出错后重新建立连接,应对临时的网络波动或服务器繁忙。
  • 优雅的错误处理:区分不同类型的错误,只对超时、连接重置这类临时错误重试,其他错误直接标记失败。
  • 安全的文件删除:注释掉了os.remove,建议确认上传成功后再删除,避免上传失败但文件已经被删掉的情况。

内容的提问来源于stack exchange,提问作者chris1234

火山引擎 最新活动