Поскольку интерфейс Python к протоколу FTP очень прост, перейдем сразу к практическому примеру. Сценарий, представленный в примере 13.1, автоматически загружает и открывает удаленный файл с помощью Python. Если быть более точными, этот сценарий Python выполняет следующие действия:
1. Загружает файл изображения (по умолчанию) с удаленного сайта FTP.
2. Открывает загруженный файл с помощью утилиты, реализованной нами в главе 6 (пример 6.23).
Часть, которая выполняет загрузку, будет работать на любом компьютере, где есть Python и соединение с Интернетом. Однако вам, вероятно, придется изменить настройки в сценарии таким образом, чтобы он обращался к вашему серверу FTP и загружал ваш файл. Часть сценария, которая открывает файл, будет работать, если playfile.py поддерживает вашу платформу — смотрите подробности в главе 6 и внесите соответствующие изменения, если это необходимо.
Пример 13.1. PP4E\Internet\Ftp\getone.py
#!/usr/local/bin/python """
Сценарий на языке Python для загрузки медиафайла по FTP и его проигрывания.
Использует модуль ftplib, реализующий поддержку протокола ftp на основе сокетов. Протокол FTP использует 2 сокета (один для данных и один для управления — на портах 20 и 21) и определяет форматы текстовых сообщений, однако модуль ftplib скрывает большую часть деталей этого протокола. Измените настройки в соответствии со своим сайтом/файлом.
import os, sys
from getpass import getpass # инструмент скрытого ввода пароля
from ftplib import FTP # инструменты FTP на основе сокетов
nonpassive = False # использовать активный режим FTP?
filename = ‘monkeys.jpg‘ # загружаемый файл
dirname = ‘.’ # удаленный каталог, откуда загружается файл
sitename = ‘ftp.rmi.net‘ # FTP-сайт, к которому выполняется подключение
userinfo = (‘lutz’, getpass(‘Pswd?’)) # () — для анонимного доступа if len(sys.argv) > 1: filename = sys.argv[1] # имя файла в командной строке?
print(‘Downloading…’)
localfile = open(filename, ‘wb’) # локальный файл, куда сохраняются данные connection.retrbinary(‘RETR ‘ + filename, localfile.write, 1024) connection.quit()
localfile.close()
if input(‘Open file?’) in [‘Y’, ‘y’]:
from PP4E.System.Media.playfile import playfile playfile(filename)
Большинство деталей реализации протокола FTP инкапсулировано в импортируемом модуле Python ftplib. Данный сценарий использует самые простые интерфейсы ftplib (остальные мы увидим чуть позже, в этой же главе), но они являются достаточно представительными для модуля в целом.
Чтобы открыть соединение с удаленным (или локальным) сервером FTP, нужно создать экземпляр класса ftplib.FTP, передав ему имя (доменное или IP-адрес) компьютера, с которым нужно соединиться:
connection = FTP(sitename) # соединиться с FTP—сайтом
Если при этом вызове не возбуждается исключение, полученный объект FTP экспортирует методы, соответствующие обычным операциям FTP. Сценарии Python действуют подобно типичным программам FTP— клиентов — нужно просто заменить обычные вводимые или выбираемые команды вызовами методов:
connection.login(*userinfo) # по умолчанию анонимный доступ connection.cwd(dirname) # передача порциями по 1 Кбайту
После подключения производится регистрация и переход в удаленный каталог, где находится требуемый файл. Метод login позволяет передавать дополнительные необязательные аргументы, определяющие имя пользователя и пароль. По умолчанию выполняется анонимная регистрация FTP. Обратите внимание на флаг nonpassive, используемый в этом сценарии:
if nonpassive: # использовать активный режим FTP,
connection.set_pasv(False) # если этого требует сервер
Если этот флаг установлен в значение True, сценарий будет осуществлять передачу файла не в пассивном режиме FTP, используемом по умолчанию, а в активном. Мы не будем углубляться здесь в детали отличий между режимами (режим определяет, с какой стороны соединения производится выбор номера порта для передачи). Но если у вас возникнут проблемы с передачей файлов с помощью какого-либо сценария FTP из этой главы, попробуйте сначала использовать активный режим. В Python 2.1 и более поздних версиях, по умолчанию используется пассивный режим FTP. Теперь откроем локальный файл, куда будет сохраняться содержимое принимаемого файла, и выполним загрузку:
localfile = open(filename, ‘wb’)
connection.retrbinary(‘RETR ‘ + filename, localfile.write, 1024)
После перехода в целевой каталог вызывается метод retrbinary для загрузки целевого файла с сервера в двоичном режиме. Для завершения вызова retrbinary требуется некоторое время, поскольку должен быть загружен большой файл. Метод принимает три аргумента:
• Строка команды FTP, в данном случае строка RETR имя_файла, являющаяся стандартным форматом загрузки по FTP.
• Функция или метод, которым Python передает каждый блок загруженных байтов файла, — в данном случае метод write вновь созданного и открытого локального файла.
• Размер этих блоков байтов. В данном случае каждый раз загружается 1024 байта, но если этот аргумент опущен, используется значение по умолчанию.
Так как этот сценарий создает локальный файл с именем localfile, таким же, как у загружаемого удаленного файла, и передает его метод write методу получения FTP, содержимое удаленного файла автоматически окажется в локальном файле на стороне клиента после завершения загрузки.
Обратите внимание, что этот файл открывается в двоичном режиме wb: если этот сценарий выполняется в Windows, нужно избежать автоматического преобразования байтов \n в последовательности байтов \r\n — как мы видели в главе 4, эта операция автоматически выполняется в Windows при записи в файлы, открытые в текстовом режиме w. Нам также необходимо избежать проблем с кодировкой Юникода в Python 3.X — как мы знаем из той же главы 4, при записи в текстовом режиме выполняется кодирование строк, что является излишним для двоичных файлов, таких как изображения. А кроме того, текстовый режим не позволил бы библиотечному методу retrbinary передавать строки bytes методу write текстового файла, поэтому режим wb фактически является здесь единственно допустимым (подробнее о режимах открытия файлов для записи мы поговорим ниже).
Наконец, вызывается метод FTP quit, чтобы разорвать соединение с сервером, и с помощью метода close вручную закрывается локальный файл, чтобы вытолкнуть выходные буферы на диск и обеспечить возможность дальнейшей обработки файла (без вызова close части файла могут остаться в выходных буферах):
connection.quit()
localfile.close()
Вот и все, что нужно сделать. Все детали протокола FTP, сокетов и работы в сети скрыты за интерфейсом модуля ftplib. Ниже приводятся результаты работы этого сценария в Windows 7 — после загрузки файл изображения появляется на экране моего ноутбука, в окне программы просмотра, как показано на рис. 13.1. Измените имя сервера и файла в этом сценарии, чтобы опробовать его со своим сервером и своим файлом, и обязательно проверьте, чтобы переменная окружения PYTHONPATH включала путь к корневому каталогу примеров PP4E, так как здесь выполняется импортирование модулей из дерева каталогов с примерами:
C:\…\PP4E\Internet\Ftp> python getone.py Pswd?
Connecting…
Downloading…
Open file?y
Обратите внимание, что здесь для запроса пароля FTP используется стандартная функция Python getpass.getpass. Подобно встроенной функции input, она выводит приглашение к вводу и читает строку, вводимую пользователем в консоли. В отличие от input, функция getpass не выводит вводимые символы на экран (смотрите пример moreplus перенаправления потоков ввода-вывода в главе 3, где демонстрируются похожие инструменты). Этот прием удобно использовать для сокрытия паролей от постороннего глаза. Но будьте внимательны — графический интерфейс IDLE после предупреждения выводит все символы пароля!
Обратите особое внимание, что этот обычный в остальных отношениях сценарий Python способен получать данные с произвольных удаленных сайтов FTP и компьютеров. При наличии ссылки с помощью подобных интерфейсов сценариями Python может быть получена любая информация, опубликованная на сервере FTP в Сети.
Рис. 13.1. Файл изображения, загруженный по FTP и открытый на локальном компьютере
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011