使用 ftplib 可恢复 FTP 文件夹上传

resumable FTP folder upload with ftplib

我是 FTP 的新手。我使用 ftplib 编写了一个 Python 脚本,以在 Upload folders from local system to FTP using Python script 上使用 TLS 加密将大量文件夹和文件递归上传到 FTP 服务器。到目前为止,该脚本一直有效,直到与服务器的连接超时,这种情况经常发生。当脚本遇到下面列出的错误时,它也会重新连接到服务器。 但是,我不知道如何在重新连接时在复杂的文件夹结构中准确地恢复中断的 FTP 上传。虽然我找到了针对单个文件 () 的可恢复 FTP 上传的解决方案,但我无法弄清楚如何在复杂且深度嵌套的文件夹结构中恢复中断的文件和文件夹上传。

重新连接时,脚本需要在上传时从文件夹结构中停止的位置恢复,而不是再次返回所有已上传的目录。有没有一种有效的方法来做到这一点?这是我目前所拥有的(服务器、路径等是占位符)。

在此先感谢您的帮助!

import ftplib
import os
import ssl
import time

class ReusedSslSocket(ssl.SSLSocket):
    def unwrap(self):
        pass

class MyFTP_TLS(ftplib.FTP_TLS):
    """Explicit FTPS, with shared TLS session"""
    def ntransfercmd(self, cmd, rest=None):
        conn, size = ftplib.FTP.ntransfercmd(self, cmd, rest)
        if self._prot_p:
            conn = self.context.wrap_socket(conn,
                                            server_hostname=self.host,
                                            session=self.sock.session)  # reuses TLS session            
            conn.__class__ = ReusedSslSocket  # we should not close reused ssl socket when file transfers finish
        return conn, size

session = MyFTP_TLS(server, username, password, timeout=None)
session.prot_p()

def uploadFolder(path):
    files = os.listdir(path)
    os.chdir(path)
    for f in files:
        if os.path.isfile(path + r'\{}'.format(f)):
            fh = open(f, 'rb')
            session.storbinary('STOR %s' % f, fh)
            fh.close()
        elif os.path.isdir(path + r'\{}'.format(f)):
            session.mkd(f)
            session.cwd(f)
            uploadFolder(path + r'\{}'.format(f))
    session.cwd('..')
    os.chdir('..')

def reset_connection(pwd=password):
    print("Attempting FTP reconnect")
    try:
        session.quit()
    except (ConnectionResetError, WindowsError, OSError) as e:
        print(e)
    time.sleep(2)
    session.connect(server, timeout=None)
    session.login(username, password)
    session.prot_p()
    uploadFolder(path)
try:
     uploadFolder(path)
except (ConnectionResetError, WindowsError, OSError) as e:
    print(e)
    reset_connection(password)

添加一个尝试,除了你上传文件的地方,如果出现问题,重新连接并再次上传文件。您将需要一个循环来继续重试:

    while True:
        try:
            fh = open(f, 'rb')
            session.storbinary('STOR %s' % f, fh)
            fh.close()
            break
        except:
            print(f'Error while uploading {path}'

处理特定错误而不是所有错误。您可能想要添加一个重试计数器,之后它就会崩溃。

另一种解决方案是跟踪列表中成功上传的每个文件的完整路径,并在崩溃时保存列表。然后您可以从文件中读取列表并重新启动该过程。在上传每个文件之前检查它是否在列表中。启动并读取列表或退出并成功完成所有上传后,删除文件。