Возобновить загрузку по FTP после тайм-аута
Я загружаю файлы с ненадежного FTP-сервера, который часто прерывается во время передачи файлов, и мне было интересно, есть ли способ восстановить соединение и возобновить загрузку. Я использую ftplib Python. Вот код, который я использую:
#! /usr/bin/python
import ftplib
import os
import socket
import sys
#--------------------------------#
# Define parameters for ftp site #
#--------------------------------#
site = 'a.really.unstable.server'
user = 'anonymous'
password = 'someperson@somewhere.edu'
root_ftp_dir = '/directory1/'
root_local_dir = '/directory2/'
#---------------------------------------------------------------
# Tuple of order numbers to download. Each web request generates
# an order numbers
#---------------------------------------------------------------
order_num = ('1','2','3','4')
#----------------------------------------------------------------#
# Loop through each order. Connect to server on each loop. There #
# might be a time out for the connection therefore reconnect for #
# every new ordernumber #
#----------------------------------------------------------------#
# First change local directory
os.chdir(root_local_dir)
# Begin loop through
for order in order_num:
print 'Begin Proccessing order number %s' %order
# Connect to FTP site
try:
ftp = ftplib.FTP( host=site, timeout=1200 )
except (socket.error, socket.gaierror), e:
print 'ERROR: Unable to reach "%s"' %site
sys.exit()
# Login
try:
ftp.login(user,password)
except ftplib.error_perm:
print 'ERROR: Unable to login'
ftp.quit()
sys.exit()
# Change remote directory to location of order
try:
ftp.cwd(root_ftp_dir+order)
except ftplib.error_perm:
print 'Unable to CD to "%s"' %(root_ftp_dir+order)
sys.exit()
# Get a list of files
try:
filelist = ftp.nlst()
except ftplib.error_perm:
print 'Unable to get file list from "%s"' %order
sys.exit()
#---------------------------------#
# Loop through files and download #
#---------------------------------#
for each_file in filelist:
file_local = open(each_file,'wb')
try:
ftp.retrbinary('RETR %s' %each_file, file_local.write)
file_local.close()
except ftplib.error_perm:
print 'ERROR: cannot read file "%s"' %each_file
os.unlink(each_file)
ftp.quit()
print 'Finished Proccessing order number %s' %order
sys.exit()
Ошибка, которую я получаю: socket.error: [Errno 110] Тайм-аут соединения
Любая помощь с благодарностью.
3 ответа
Возобновление загрузки через FTP с использованием только стандартных средств (см. RFC959) требует использования режима блочной передачи (раздел 3.4.2), который можно установить с помощью MODE B
команда. Хотя эта функция технически необходима для соответствия спецификации, я не уверен, что все программное обеспечение сервера FTP реализует ее.
В режиме блочной передачи, в отличие от режима потоковой передачи, сервер отправляет файл порциями, каждый из которых имеет маркер. Этот маркер может быть повторно отправлен на сервер для перезапуска неудачной передачи (раздел 3.5).
В спецификации сказано:
[...] предусмотрена процедура перезапуска для защиты пользователей от серьезных системных сбоев (включая сбои хоста, FTP-процесса или базовой сети).
Тем не менее, AFAIK, спецификация не определяет требуемый срок службы маркеров. Это говорит только следующее:
Информация маркера имеет значение только для отправителя, но должна состоять из печатных символов на языке по умолчанию или согласованном языке управляющего соединения (ASCII или EBCDIC). Маркер может представлять счетчик битов, счетчик записей или любую другую информацию, с помощью которой система может идентифицировать контрольную точку данных. Получатель данных, если он реализует процедуру перезапуска, затем маркирует соответствующую позицию этого маркера в принимающей системе и возвращает эту информацию пользователю.
Следует с уверенностью предположить, что серверы, реализующие эту функцию, будут предоставлять маркеры, действительные между сеансами FTP, но ваш пробег может отличаться.
Простой пример реализации возобновляемой загрузки по FTP с использованием Python ftplib:
def connect():
ftp = None
with open('4gb', 'wb') as f:
while (not finished):
if ftp is None:
print("Connecting...")
FTP(host, user, passwd)
try:
rest = f.tell()
if rest == 0:
rest = None
print("Starting new transfer...")
else:
print(f"Resuming transfer from {rest}...")
ftp.retrbinary('RETR 4gb', f.write, rest=rest)
print("Done")
finished = True
except Exception as e:
ftp = None
sec = 5
print(f"Transfer failed: {e}, will retry in {sec} seconds...")
time.sleep(sec)
Рекомендуется более детальная обработка исключений.
Аналогично для загрузок:
обработка отключений в Python ftplib FTP передает загрузку файлов.
Чтобы сделать это, вам нужно будет сохранить прерванную загрузку, затем выяснить, какие части файла вам не хватает, загрузить эти части и затем соединить их вместе. Я не уверен, как это сделать, но есть менеджер загрузок для Firefox и Chrome, который называется DownThemAll, который делает это. Хотя код не написан на python (я думаю, что это JavaScript), вы можете посмотреть на код и посмотреть, как он это делает.
DownThemll - http://www.downthemall.net/