.NET и С# глазами дельфиста
Задача
Напишем программу для обновления сайта, выложенного на ftp. Наша утилита должна будет анализировать изменения,
произошедшие в локальной копии сайта и обновить только те файлы, которые отсутствуют или изменились со времени
последнего обновления.
- Для контроля изменений мы создадим файл, где для каждого обрабатываемого файла сохраним контрольную информацию
- При проверке необходимости изменений мы сравним текущую контрольную информацию с сохраненной. Если значения различны
или сохраненной информации нет (новый файл), то файл должен быть синхронизирован
- Нам потребуется список исключений, в том числе и масочных, чтобы фильтровать синхронизируемые файлы
Инструменты
- Freeware IDE SharpDeveloper 2.1.0.2429 - 3.2.1.6466
Разработка
Для хранения информации о синхронизации будем использовать простой текстовый файл из строк формата name=value.
Для сопоставления информации файлу в name запишем его относительный путь, а в value нужную нам информацию (правда это
приведет к невозможности использовать знак = внутри имени файла). Служебную информацию будем также хранить в этом
файле в начале, предварив каждую строку служебной информации знаком ?, который не может встретиться в имени файла.
Пример:
?D=X:\Docs\My\site\
...
index.htm=945985558
...
art\gnom.htm=885433848
...
Для работы с файлами будем использовать классы из System.IO. В частности, отсканировать имена всех файлов в массив
очень просто при помощи метода Directory.GetFiles. У такого простого подхода есть недостатки - при большем числе
исключений по иерархии директорий мы вынуждены будем потратить время на сканирование не нужных нам файлов.
В качестве контрольной информации используем время изменения файла File.GetLastWriteTimeUtc (UTC позволит нам
избежать проблем с переходом на летнее\зимнее время). Альтернативой могло бы быть вычисление Hash функции, например,
CRC32 от содержимого, но это сильно замедлит обработку данных.
Для формирования списка различий возьмем сохраненный список файлов (Old) и сформируем текущий (New). Если эти два
списка отсортировать и анализировать последовательно, то можно получить список Diff файлов необходимых для
синхронизации:
- Если текущие значения в New и Old совпадают, то значит файл не изменился (и имя и время совпадают)
- Если текущие значения в New и Old не совпадают, но имена файлов в этих значениях одинаковы, то значит файл
изменился (время изменения изменилось)
- Если между текущими значениями верно условие New > Old, значит в сохраненном файле есть информация, которой уже
нет на диске, те файл был удален. Соответсвующий файл надо удалить, а значение Old пропустить не меняя значения New
- Соответственно, если между текущими значениями верно условие New < Old, значит на диске появился новый файл.
Такой файл надо скопировать, а значение New пропустить не меняя Old
Для отсылки изменений на ftp используем класс WebClient из System.Net. После установки свойства BaseAddress в нужный
нам адрес ftp, можно использовать метод UploadFile для загрузки и обновления нужных файлов (относительный путь
подойдет для копирования файла в нужную директорию ftp). Однако, этого недостаточно для того, чтобы создавать структуру
каталогов на ftp и удалять файлы с ftp. Для установки авторизации используем объект NetworkCredential.
Единственная настройка программы не хранимая в файле - список последних файлов синхронизации. Его сохраняем в реестре,
используя класс Registry из Microsoft.Win32. Однако нехорошо хранить пароли ftp в открытом виде в файле, поэтому их
так же сохраняем в реестре использовав в качестве ключа хэш строки с url и именем пользователя. Также пароль конвертируем
в base64 использую System.Text.
Недоделки
- Разбираюсь с удалением и создание директорий на ftp - придется использовать FTPWebRequest...
Архив программы с исходником
Наблюдения
- Напрягает писать скобочки у методов без аргументов, а еще ; перед else - какая гадость :)))
- Как продолжить исполнение в случае ошибки? - не нашел, всегда происходит останов...
- Наличие LinkLabel порадовало
- Где же Align контролов? Пришлось ручками выставлять размер и использовать якоря (Anchor)
- Не дописал конструкцию в исходнике - решил еще раз взглянуть на свойства одного из контролов...
А с кривым исходником формочку не откроешь - убожество
- Я так любил переключаться по иерархии контролов на формочке при помощи клавиши Esc...
- Не хватает подсказок над контролами при дизайне формочки. Компоненты в отдельной области это, наверно,
правильно, но непривычно :)
- Очень не хватает StringList :) и не только мне.
Одними массивами сыт не будешь. Есть похожие ArrayList и более типизированный StringCollection, но, всеравно,
загрузку\сохранение из текстового файла пришлось реализовать частично ручками (но, к слову сказать, не факт, что
итоговое количество кода сильно возрасло)
- Работа с файлами: как получить Relative path - не нашел. IncludeTrailingBackSlash - ручками.
- Старая добрая 32бит дата файла почила в бозе... Зато есть время UTC.
- Хотелось сравнения с простой файловой маской (MatchesMask) - не нашлось. Вернее нашлось, но где-то в сборках PowerShell.
В тоже время полноценные регулярные выражения не особо были нужны - написал ручкми простую фукцию...
- Не сразу нашел hint. Вобще поход, когда набор свойств меняется в зависимости от контекста, интересен,
но, как минимум, не привычен.
- Хотел добавить строчку в MultiLine Edit (Memo)... Долго вспоминал escape последовательность \r\n (одного \n мало -
перевод строки появляется вместе с каким-то псевдографическим символом)...
- Аналог Application.ProcessMessages - Application.DoEvents()
Наблюдения SharpDeveloper 2.1
- Где watch? Локальные переменные есть, а посмотреть значение свойства уже нельзя
- SharpDeveloper очень не любит показывать большие массивы в отладчике - на массиве из 40000 элементов все упало :)