Python:ftplib在传输结束时挂起

时间:2013-10-30 20:01:06

标签: python hang ftplib

我一直在搜索这几天,但还没有找到答案。

我试图从FTP下载视频文件,我的脚本检查服务器,将nlist()与从文本文件解析的已下载文件列表进行比较,然后创建一个新的文件列表以获取和迭代它下载每个文件,断开与服务器的连接并重新连接下一个文件(我认为服务器超时可能是一个问题所以我在每个文件下载后退出()连接)。

这适用于前几个文件但是只要我点击一个超过5分钟的文件,fitlib就会在传输结束时挂起(我可以在资源管理器中看到该文件的大小正确所以下载已经完成但它似乎没有得到消息并继续下一个文件)

任何帮助将不胜感激,我的代码如下:

newPath = "Z:\\pathto\\downloads\\"

for f in getFiles:
    print("Getting " + f)

for f in getFiles:

    fil = f.rstrip()
    ext = os.path.splitext(fil)[1]
    if ext in validExtensions:
        print("Downloading new file: " + fil)
        downloadFile(fil, newPath)

这里是download.py

from ftplib import FTP
def downloadFile(filename, folder):
    myhost = 'host'
    myuser = 'user'
    passw = 'pass'
    #login
    ftp = FTP(myhost,myuser,passw)
    localfile = open(folder + filename, 'wb')
    ftp.retrbinary("RETR " + filename, localfile.write, 1024)
    print("Downloaded " + filename)
    localfile.close()
    ftp.quit()

1 个答案:

答案 0 :(得分:19)

如果没有更多信息,我实际上无法调试您的问题,因此我只能提出最一般的答案。这对你来说可能不是必需的,但对任何人来说都可能就足够了。

retrbinary将阻止,直到整个文件完成。如果超过5分钟,则整个5分钟内不会通过控制通道发送任何内容。您的客户端是超时控制通道,还是服务器。因此,当您尝试挂断ftp.quit()时,它将永久挂起或引发异常。

您可以使用FTP构造函数上的timeout参数控制您方的超时。某些服务器支持IDLE命令,允许您设置服务器端超时。但是,即使适当的结果是可行的,你如何在一开始就选择适当的超时?

您真正想要做的是防止控制套接字在数据套接字上发生传输时超时。但是怎么样?如果您在回调函数中经常使用ftp.voidcmd('NOOP'),那么这足以使连接保持活动状态......但它也会强制您阻塞直到服务器响应NOOP,在数据传输完成之前,许多服务器都不会这样做,这意味着您将永远阻止(或直到​​不同的超时)并且不会获取数据。

处理两个套接字但没有一个阻塞的标准技术是像select.select或线程这样的多路复用器。你可以在这里做到这一点,但你必须放弃使用简单的retrbinary接口,而是使用transfercmd明确地获取数据套接字。

例如:

def downloadFile(…):
    ftp = FTP(…)
    sock = ftp.transfercmd('RETR ' + filename)
    def background():
        f = open(…)
        while True:
            block = sock.recv(1024*1024)
            if not block:
                break
            f.write(block)
        sock.close()
    t = threading.Thread(target=background)
    t.start()
    while t.is_alive():
        t.join(60)
        ftp.voidcmd('NOOP')

另一种解决方案是一次读取20MB,然后拨打ftp.abort(),并使用rest参数恢复每个新retrbinary的转移,直到达到文件的结尾。但是,ABOR可能会永远挂起,就像NOOP一样,因此不能保证任何内容 - 更不用说服务器不必响应它了。

可以做的只是关闭整个连接(不是quit,而是close)。这对服务器来说不是很好,并且可能会导致重新发送一些浪费的数据,并且如果你过快地终止套接字,也可能阻止TCP按照通常的速度升级到全速。但它应该有用。

请参阅this answer - 并注意到它需要针对您的特定损坏服务器进行一些测试,以确定哪种变体(如果有)可以正常有效地工作。