На
днях понадобилось написать небольшую программку. Программа проста как
валенок: скачать файл с фтп, откорректировать его должным образом,
залить на другой фтп. В целях экономии времени выбор языка пал на Делфи,
ведь там есть удобный компонент работы сidFtp!
Подумано
- сделано, быстро вояю программку и довольный смотрю на результат: он
же, мягко говоря, сильно отличается от ожидаемого... Т.е. на выходе файл
совсем не такой как должен быть. Ну, что поделать, начинаю усердно
отлаживать код редактирования скаченного файла...
По
прошествии нескольких часов я потерял веру в жизнь и законы физики: в
коде все абсолютно верно! Значит, ошибка кроется в другом. В чем
же?Обращаю свой взор на скачанный файл... Ах тыж екарный через плечо
бабай, в рот мне ноги, WTF?!! Размер скачанных файлов с фтп отличается
от оригинального! И ладно бы они были меньше, так ведь они больше!! Не
веря свои глазам скачиваю файлы вручную и прогоняю на них свой код. Все
работает как часы!
Ну... что поделать? Завариваем крепкий чай и вперед!
Собственно сам код скачки файлов:
IdFTP1.Get(FileNames[i],GetCurrentDir()+'\'+FileNames[i],false, true);
FileNames представляет собой TStringList и содержит имена файлов на фтп-сервере.
Итак, для начала посмотрим чем же скачанные нами файлы отличаются от оригинальных:
Как
видим, размер на который отличаются файлы разный, но примерно
составляет 155кб (около 2% от общего объема). Откуда? Не понятно.
Сравниваем содержимое файла:
Мистика! Файлы действительно отличаются по содержимому!
Тут
стоит отметить что компонент idFtp может скачивать файлы в бинарном и
текстовом виде. За это отвечает свойство TransferType, которое может
принимать значения ftASCII и ftBinary. Так может дело в этом? Нет. В
обоих случаях результат один и тот же.
Более
детальное изучение дает еще более удивительный результат: содержимое
файлов одинаковое, отличаются символы переноса строк! Точнее в скачанном
файле перед каждым переносом строк (0A) добавляется еще один (0D).
Педивикия по этому поводу говорит следующее:
Системы, основанные на ASCII или совместимом наборе символов, используют или LF0x0A), или CR (возврат каретки, 0x0D) по отдельности, или последовательность CR+LF. Эти названия основаны на командах принтера: перевод строки означает, что одна строка на бумаге должна быть перенесена при печати, а возврат каретки означает, что каретка печатающего устройства должна вернуться к началу текущей строки. Символы:
- LF (ASCII 0x0A) используется в Multics, UNIX, UNIX-подобных операционных системахGNU/Linux, AIX, Xenix, Mac OS X, FreeBSD и др.), BeOS, Amiga UNIX, RISC OS и других;
- CR (ASCII 0x0D) используется в 8-битовых машинах Commodore, машинах TRS-80, Apple II, системах Mac OS до версии 9 и OS-9;
- CR+LF (ASCII 0x0D 0x0A) используется в DEC RT-11 и большинстве других ранних не-UNIX- и не-IBM-систем, а также в CP/M, MP/M (англ.), MS-DOS, OS/2, Microsoft Windows, Symbian OS, протоколах Интернет.
Таким
образом, я установил откуда растут ноги моего бага: в извечной проблеме
Unix vs Windows. Кто переносил код написанный в Delphi 7 в Delphi 2009
или Delphi 2010, знает что основная проблема переноса кроется в том, что
многие методы использующие кодировку по умолчанию Windows стали
использовать кодировку Unix. Скорее всего мои проблемы из той же серии и
данный код без проблем заработает как надо в Delphi 7.
На
вскидку, данную проблему можно решить 3-мя способами: 1) Скомпилировать
.exe в Delphi 7; 2) Лезть в исходный код компонента idFtp и разбираться
с ним; 3) Сделать "костыль" заменяющий "0x0D0x0A" на "0x0A"
Каждый способ имеет минусы: 1) У меня Windows 7, на которую Delphi 7 без бубна не ставится 2) Лень; 3) +100 к "индусости" кода.
Лично
мне хотелось поскорее закрыть этот вопрос, потому я быстро установил
Delphi7 и скомпилировал код там. Если у кого есть предложения получше -
добро пожаловать в комменты :)
P.S.
Чего я так и не понял, зачем борландцам понабилось городить не пойми
что и почему функция Get не может просто скопировать файл байт-в-байт.
о божэ мой я уже потеряла надежду понять что за фигня происходит, спасибо за объяснения!
ОтветитьУдалитьвсе куда проще, нужно всего лишь поставить FFTP.TransferType := ftBinary; в то время как в Indy10 этот параметр по умолчанию ftASCII, т.е. передается текст который потом искажается в части переносов строк
ОтветитьУдалитьда, ошибка 100%, к сожалению бинарный режим хорошо работает только в справке к программе, в чистой теории, по факту, все как написано в статье. баг, сводящий на "нет" работу компонента. Сборка D2010 Build 25826.
ОтветитьУдалить