Загрузка файлов: двоичный и текстовый режимы

zagruzka fajlov dvoichnyj i tekstovyj rezhimy Сценарии на стороне клиента

Двоичные файлы загружаются с помощью метода retrbinary, с которым мы познакомились ранее, и локального режима открытия wb. Этот режим необходим, чтобы обеспечить возможность передачи строк bytes методу write, вызываемому методом retrbinary, а также подавить преобразование байтов конца строки и кодирование символов Юникода. Опять же текстовый режим в Python 3.X требует, чтобы в этом режиме в файлы записывался текст, который можно кодировать в указанную кодировку, а попытка записи двоичных данных, таких как изображения, может вызывать ошибки кодирования. Этот сценарий может выполняться в Windows или в Unix-подобных системах, и было бы нежелательно, чтобы в Windows байты \n в изображениях замещались парой байтов \r\n. Здесь не используется третий аргумент, определяющий размер блока, — по умолчанию он принимает достаточно разумное значение.

Для загрузки текстовых файлов этот сценарий использует другой метод — retrlines, передавая ему функцию, которая должна вызываться для каждой загруженной строки текстового файла. Функция-обработчик строки текста обычно просто принимает строку str и записывает ее в локальный текстовый файл. Но обратите внимание, что функция-обработчик, создаваемая здесь lambda-выражением, добавляет также в конец переданной ей строки символ перевода строки \n. Метод Python retrlines удаляет из строк все символы перевода строки, чтобы обойти различия между платформами. Добавляя \n, сценарий обеспечивает добавление правильной последовательности символов перевода строки для той платформы, на которой выполняется сценарий (\n или \r\n).

Конечно, чтобы такое автоматическое отображение символов \n действовало в сценарии, необходимо также открывать выходные текстовые файлы в текстовом режиме w, а не в режиме wb — при записи данных в файл в Windows происходит отображение \n в \r\n. Как уже обсуждалось ранее, текстовый режим также подразумевает, что при вызове метода write файла из метода retrlines ему будет передаваться строка str, и при записи текст будет кодироваться с применением указанной кодировки.

Обратите внимание, что в вызове функции open, открывающей выходной текстовый файл, вместо кодировки по умолчанию мы явно используем схему ко диро ва ния, извлекая ее из объекта connection класса FTP. Без этого сценарий завершался с ошибкой UnicodeEncode- Error при попытке загрузить некоторые файлы с моего сайта. В методе retrlines объект FTP читает данные из удаленного файла через сокет, используя файл-обертку в текстовом режиме, и явно указывает схему кодирования для декодирования байтов. Поскольку в любом случае объект класса FTP не может предложить ничего лучше, мы используем эту же кодировку и при открытии выходного файла.

Для декодирования получаемого текста (а также для кодирования текста при передаче) объекты FTP по умолчанию используют кодировку latin1, но ее можно изменить, записав требуемое имя кодировки в атрибут encoding. Локальные текстовые файлы сохраняются в кодировке, используемой модулем ftplib, поэтому они совместимы с кодировками, применяемыми к текстовым данным, которые производятся и передаются этим модулем.

Мы могли бы также попробовать перехватывать исключения кодирования символов Юникода для файлов, несовместимых с кодировкой, используемой объектом FTP, но при тестировании в Python 3.1 выяснилось, что исключения оставляют объект FTP в неисправном состоянии. Как вариант, мы могли бы также использовать двоичный режим wb для выходных локальных текстовых файлов и вручную кодировать строки с помощью метода line.encode, или просто использовать метод retrbinary и двоичный режим выходных файлов во всех случаях, но в обоих ситуациях мы лишились бы возможности отображать символы конца строки переносимым способом — потеряется весь смысл различения типов файлов в этом контексте.

Лучше один раз увидеть, чем сто раз услышать. Ниже приводится команда, которую я использую для загрузки всего моего веб-сайта поддержки книги с сервера моего провайдера на ноутбук с Windows за один шаг:

C:\\PP4E\Internet\Ftp\Mirror> downloadflat.py test Password for lutz on home.rmi.net:

Clean local directory first? y connecting

deleting local 2004-longmont-classes.html

deleting local 2005-longmont-classes.html

deleting local 2006-longmont-classes.html

deleting local about-hopl.html

deleting local about-lp.html

deleting local about-lp2e.html deleting local about-pp-japan.html

…часть строк опущена…

downloading 2004-longmont-classes.html to test\2004-longmont-classes.html as text

downloading 2005-longmont-classes.html to test\2005-longmont-classes.html as text

downloading 2006-longmont-classes.html to test\2006-longmont-classes.html as text

downloading about-hopl.html to test\about-hopl.html as text

downloading about-lp.html to test\about-lp.html as text

downloading about-lp2e.html to test\about-lp2e.html as text

downloading about-pp-japan.html to test\about-pp-japan.html as text

…часть строк опущена…

downloading ora-pyref4e.gif to test\ora-pyref4e.gif as image

downloading ora-lp4e-big.jpg to test\ora-lp4e-big.jpg as image

downloading ora-lp4e.gif to test\ora-lp4e.gif as image

downloading pyref4e-updates.html to test\pyref4e-updates.html as text

downloading lp4e-updates.html to test\lp4e-updates.html as text

downloading lp4e-examples.html to test\lp4e-examples.html as text downloading LP4E-examples.zip to test\LP4E-examples.zip as application Done: 297 files downloaded.

Эта процедура может занять несколько минут, в зависимости от размера вашего сайта и скорости вашего соединения (это связано с ограничениями скорости передачи в сети, и для передачи моего веб-сайта на мой ноутбук с беспроводным подключением обычно требуется примерно две-три минуты). Однако такая процедура значительно точнее и проще, чем загрузка файлов вручную. Этот сценарий обходит весь список файлов на веб-сервере, возвращаемый методом nlst, и поочередно загружает каждый из них по протоколу FTP (то есть через сокеты). Для имен, очевидно указывающих на текстовые данные, используется текстовый режим передачи, а для остальных — двоичный режим.

Прежде чем запускать сценарий таким способом, я проверяю, чтобы начальные операции присваивания в нем отражали участвующие в обмене компьютеры, а затем запускаю его из локального каталога, в который хочу поместить копию сайта. Поскольку каталог для загрузки обычно отличается от каталога, где находится сам сценарий, интерпретатору необходимо указать полный путь к файлу сценария. Например, когда я выполняю этот сценарий в сеансе Telnet или SSH, каталог выполнения и каталог, где находится сам сценарий, отличаются, но сценарий работает одинаково.

Если вы решите удалить локальные файлы из каталога загрузки, то можете получить на экране серию сообщений «deleting local…» и только потом строки «downloading.»: это автоматически удалит весь лишний мусор, оставшийся от предыдущих операций загрузки. А если вы ошибетесь при вводе пароля, Python возбудит исключение — мне иногда приходится запускать сценарий повторно (и вводить пароль медленнее):

C:\\PP4E\Internet\Ftp\Mirror> downloadflat.py test

Password for lutz on home.rmi.net:

Clean local directory first?

connecting

Traceback (most recent call last):

File "C:\\PP4E\Internet\Ftp\Mirror\downloadflat.py", line 29, in <module>

connection.login(remoteuser, remotepass) # зарегистрироваться

# с именем/паролем

File "C:\Python31\lib\ftplib.py", line 375, in login

if resp[0] == ‘3’: resp = self.sendcmd(‘PASS ‘ + passwd)

File "C:\Python31\lib\ftplib.py", line 245, in sendcmd return self.getresp()

File "C:\Python31\lib\ftplib.py", line 220, in getresp raise error_perm(resp)

ftplib.error_perm: 530 Login incorrect.

Следует отметить, что этот сценарий частично настраивается операциями присваивания, производимыми в начале файла. Кроме того, пароль и признак необходимости удаления файлов указываются при интерактивном вводе, и разрешается указывать один аргумент командной строки — имя локального каталога для загрузки файлов (по умолчанию используется «.», то есть каталог, в котором выполняется сценарий). Можно было бы использовать аргументы командной строки для настройки других параметров загрузки, но благодаря простоте Python и отсутствию этапов компиляции/сборки изменять настройки в тексте сценариев Python обычно не труднее, чем вводить слова в командной строке.

Для проверки возможных различий между версиями файлов после такой пакетной загрузки или выгрузки можно воспользоваться сценарием diffall, представленным в главе 6, в примере 6.12. Например, я обнаруживаю файлы, изменившиеся с течением времени из-за обновлений на нескольких платформах, сравнивая загруженную локальную копию моего веб-сайта с помощью такой команды: C:\…\ PP4E\Internet\Ftp> ..\..\System\Filetools\diffall.py Mirror\test C:\…\ Websites\public_html. Подробности об этом инструменте смотрите в главе 6, а пример результатов, получаемых с его помощью, смотрите и в файле diffall.out.txt, находящемся в каталоге diffs в дереве примеров. Различия между текстовыми файлами в основном обусловлены либо наличием завершающего символа перевода строки в конце файла, либо различиями между символами перевода строки, возникшими в результате передачи в двоичном режиме, которые команда fc в Windows и FTP-серверы не замечают.

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий