我一直在尝试解决从ftp / ftps下载文件的问题。文件下载成功,但文件下载完成后不执行任何操作。没有发生错误,可以提供有关该问题的更多信息。 我尝试在stackoverflow上搜索这个,并发现这个link谈论类似的问题陈述,看起来我面临类似的问题,虽然我不确定。在解决问题方面需要更多帮助。
我尝试将FTP连接超时设置为60分钟,但帮助较少。 在此之前,我使用了ftplib的retrbinary(),但同样的问题出现在那里。我试过传递不同的块大小和窗口大小但是这个问题也是可以重现的。
我正在尝试从AWS EMR群集下载大小约为3GB的文件。示例代码如下所示。
def download_ftp(self, ip, port, user_name, password, file_name, target_path):
try:
os.chdir(target_path)
ftp = FTP(host=ip)
ftp.connect(port=int(port), timeout=3000)
ftp.login(user=user_name, passwd=password)
if ftp.nlst(file_name) != []:
dir = os.path.split(file_name)
ftp.cwd(dir[0])
for filename in ftp.nlst(file_name):
sock = ftp.transfercmd('RETR ' + filename)
def background():
fhandle = open(filename, 'wb')
while True:
block = sock.recv(1024 * 1024)
if not block:
break
fhandle.write(block)
sock.close()
t = threading.Thread(target=background)
t.start()
while t.is_alive():
t.join(60)
ftp.voidcmd('NOOP')
logger.info("File " + filename + " fetched successfully")
return True
else:
logger.error("File " + file_name + " is not present in FTP")
except Exception, e:
logger.error(e)
raise
上述链接中建议的另一个选项是在下载文件的小块后关闭连接,然后重新启动连接。有人可以建议如何实现这一点,不确定如何从关闭连接之前上次停止文件下载的同一点恢复下载。这种方法是否可以完全证明下载整个文件。
我对FTP服务器级超时设置了解不多,所以不知道需要改变什么以及如何更改。我基本上想要编写一个通用的FTP下载器,它可以帮助从FTP / FTPS下载文件。
当我使用ftplib的retrbinary()方法并将调试级别设置为2时。
ftp.set_debuglevel(2)
ftp.retrbinary('RETR ' + filename, fhandle.write)
以下日志正在打印。
cmd 'TYPE I' put 'TYPE I \ r \ n' get '200 Type设置为I. \ r \ n' resp '200类型设置为I.' cmd 'PASV' put 'PASV \ r \ n' get '227进入被动模式(64,27,160,28,133,251)。\ r \ n' resp '227进入被动模式(64,27,160,28,133,251)。 cmd 'RETR FFFT_BRA_PM_R_201711.txt' put 'RETR FFFT_BRA_PM_R_201711.txt \ r \ n' get '150为FFFT_BRA_PM_R_201711.txt打开BINARY模式数据连接。\ r \ n' resp '150打开FFFT_BRA_PM_R_201711.txt的BINARY模式数据连接。'
答案 0 :(得分:1)
在做任何事情之前,请注意您的连接存在一些问题,并且诊断并解决此问题远比解决它更好。但有时候,你只需处理一个损坏的服务器,甚至发送Keepalive也无济于事。那么,你能做什么?
诀窍是一次下载一个块,然后中止下载 - 或者,如果服务器无法处理中止,请关闭并重新打开连接。
请注意,我正在使用ftp://speedtest.tele2.net/5MB.zip测试下面的所有内容,希望这不会导致一百万人开始锤击他们的服务器。当然,您需要使用实际的服务器进行测试。
REST
整个解决方案当然依赖于服务器能够恢复传输,这并不是所有服务器都可以做到的 - 尤其是当您处理严重损坏的事情时。所以我们需要测试一下。请注意,此测试将非常慢,并且在服务器上非常繁重,因此请勿使用3GB文件进行测试;找到更小的东西。此外,如果你可以在那里放一些可读的东西,它将有助于调试,因为你可能会在十六进制编辑器中比较文件。
def downit():
with open('5MB.zip', 'wb') as f:
while True:
ftp = FTP(host='speedtest.tele2.net', user='anonymous', passwd='test@example.com')
pos = f.tell()
print(pos)
ftp.sendcmd('TYPE I')
sock = ftp.transfercmd('RETR 5MB.zip', rest=pos)
buf = sock.recv(1024 * 1024)
if not buf:
return
f.write(buf)
你可能一次不会得到1MB,而是8KB以下的东西。我们假设你看到了1448,然后是2896,4344等等。
REST
获得例外,服务器无法处理恢复 - 放弃,您就会被冻结。f.seek
修复它,我可以解释一下 - 但你可能不会遇到它。ABRT
我们可以做的一件事是尝试中止下载,不重新连接。
def downit():
with open('5MB.zip', 'wb') as f:
ftp = FTP(host='speedtest.tele2.net', user='anonymous', passwd='test@example.com')
while True:
pos = f.tell()
print(pos)
ftp.sendcmd('TYPE I')
sock = ftp.transfercmd('RETR 5MB.zip', rest=pos)
buf = sock.recv(1024 * 1024)
if not buf:
return
f.write(buf)
sock.close()
ftp.abort()
您将要尝试多种变体:
sock.close
。ftp.abort
。sock.close
之后的ftp.abort
。ftp.abort
之后的sock.close
。TYPE I
移至循环之前而不是每次。有些人会提出异常。其他人似乎永远都会挂起。如果对所有8个人都这样,我们需要放弃堕胎。但如果它们中的任何一个有效,那就太好了!
另一种加快速度的方法是在中止或重新连接之前一次下载1MB(或更多)。只需替换此代码:
buf = sock.recv(1024 * 1024)
if buf:
f.write(buf)
用这个:
chunklen = 1024 * 1024
while chunklen:
print(' ', f.tell())
buf = sock.recv(chunklen)
if not buf:
break
f.write(buf)
chunklen -= len(buf)
现在,每次传输不是读取1442或8192字节,而是每次传输最多读取1MB。试着把它推得更远。
如果你的下载量达到了10MB,并且你的问题中的keepalive代码达到了512MB,但只有3GB还不够 - 你可以将两者结合起来。使用keepalive一次读取512MB,然后中止或重新连接并读取下一个512MB,直到完成。