Блог Веб-разработчика.

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

К тому же, когда работа выполняется для нового клиента, и уровень доверия еще не достаточно велик, чтобы предоставить специалисту доступ по фтп. Заказчик может попросить передать отредактированную часть сайта. И на этом этапе может возникнуть вполне резонное желание проверить: не загружаются ли вместе с файлами нечто “лишнее”, а так же хочется проверить код самих переданных файлов.

Для таких случаев было бы хорошо передать именно те файлы, которые были изменены, при этом передать их в таком виде, чтобы была сохранена структура вложенности директорий и файлов. Это вносит ясность в работе и отношениях, и простоту загрузки на сервер. А также гарантирует отсутствие ошибок, в следствие того, что файл загружен не в ту папку.

Лично мне, во всем что касается обработки файлов на локальной машине, удобнее всего работать в системе Wolfram Mathematica. Это обусловлено как большими возможностями системы, так и простотой и компактностью кода, который будет выполнять поставленные задачи. Код, продемонстрированный в данной статье, может показаться довольно простым, а кому-то даже примитивным - и в этом будет доля правды. Код простой - но делает очень приятную вещь.

Общий принцип работы.

Мы рассмотрим два основных принципа, по котором можно осуществлять экстракцию файлов: по метке, оставленной в содрежимом текстового файла и по дате последнего редактирования.

Копирование файлов основанное на метке. В процессе работы, редактируя текстовые файлы сайта необходимым образом, мы помечаем документ в любом месте каким-то ключом, обрамленным в символ комментария:

// webengineer#28012014
или
/*webengineer#28012014*/
или
<? //webengineer#28012014 ?>
или
#webengineer#28012014
или
<!--webengineer#28012014-->

Не важно каким образом разместить метку, важно чтобы она была в виде текста в файле, и не влияла на работу скрипта или отображение разметки сайта. Далее, силами wolfram mathematica, мы анализируем все файлы системы и копируем из нее только те, которые содержат эту метку.

Копирование по дате последнего редактирования - тут все ясно без комментариев. Общий принцип таков: указываем ключевую дату и выделяем только те файлы, последние даты редактирования которых стоят после ключевой.

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

Копирование по метке.

Реализация подобного кода в wolfram оказывается более чем компактная. Учитывая большие возможности системы, таких реализаций может быть, как минимум - несколько. Приведу для примера одну из них. Пойдем буквально по строчкам кода.

Действие первое. Нам нужно указать папку, файлы которой мы будем импортировать и анализировать. Для удобства мы будем указывать корневой каталог сайта на локальном сервере. Это можно сделать через функцию SystemDialogInput, с параметром “Directory”:

dirPath=SystemDialogInput["Directory"];

Действие второе. Нам необходимо импортировать список всех файлов внутри папки, в том числе тех, которые находятся во вложенных директориях. Это легко реализуется через Import:

filelist=Import[dirPath];

Действие третье. Теперь есть необходимость определить ключ, по которому мы будем соответствующие файлы выделять в отдельный список для копирования. Конечно wolfram mathematica позволяет импортировать в себя содержимое документов очень большого количества различных форматов ($ImportFormats), однако этот список не содержит в себе форматы наиболее специфичные для веб среды, такие как .php, .py, .htaccess, .js, .aspx и другие. Поэтому при попытке импортировать их содержимое - могут возникнуть проблемы. Чтобы этого не происходило - мы будем работать с двоичным представлением как искомого ключа, так и содержимого файла. Это устраняет ограничения, связанные со списком поддерживаемых форматов в wolfram. Именно поэтому ключ поиска, мы зададим не напрямую, а через ImportString и параметром Byte:

searchData = ImportString["webengineer#28012014","Byte"];

При этом строка преобразуется в список байтов, который по сути является последовательностью чисел в диапазоне от 0 до 255.

Действие четвертое. Собственно поиск файлов. Последовательно проходя по списку filelist, мы считываем в бинарном виде содержимое файлов, и тут же проверяем наличие в полученной последовательности байтов нашей искомой подпоследовательности searchData. Если подпоследовательность не найдена - то заменяем соответствующий элемент списка filelist на “0”. После чего, из полученного списка выделяем только те элементы, которые не равны “0” - это и будет список файлов, которые были нами помечены.

Есть важный нюанс. Если мы будем искать именно по списку байтов - то это будет достаточно медленно, и в случае обработки большой файловой системы, операция может занять очень много времени (порядка 30-40 минут, для файловой системы размером 7 тысяч файлов и средним весом 30-70 кб). Чтобы ускорить процесс поиска, мы предварительно преобразуем списки байтов в строки и будем осуществлять поиск именно по строкам, которые будут по прежнему представлять собой последовательность байтов. Код в таком случае ускоряется в 150-170 раз! За рекомендации по ускорению, Большое Спасибо Роману Осипову, руководителю Русскоязычной поддержки Wolfram Mathematica.

В итоге, “Действие четвертое” может быть реализовано следующим образом:

editedFiles = Cases[If[StringMatchQ[StringJoin[ToString/@BinaryReadList[FileNameJoin[{dirPath,#}],"Byte"]],___~~StringJoin[ToString/@searchData ]~~___],FileNameJoin[{dirPath,#}],0]&/@filelist,Except[0]];

Действие пятое. Определим пути, куда необходимо скопировать выделенные файлы. Для этого мы на основе списка editedFiles, получим новый список, в котором к имени корневой папки добавлен суффикс “_patch”:

patchFiles =FileNameJoin[#]&/@(FileNameSplit[#]&/@editedFiles/.FileNameTake[dirPath]->FileNameTake[dirPath]<>"_patch");

Действие шестое. Опираясь на список путей patchFiles, создаем промежуточные директории, располагающиеся между корневой папкой с суффиксом “_patch” и до конечного положения файла:

If[!DirectoryQ[DirectoryName[#]],CreateDirectory[DirectoryName[#],CreateIntermediateDirectories[Rule]True]]&/@patchFiles;

Действие седьмое. Собственно копирование файлов. Мы оба списка editedFiles и patchFiles объединяем и приводим в соответствие элементы списка друг другу, после чего последовательно передаем элементы полученного общего списка в функцию CopyFile:

CopyFile[#[[1]],#[[2]]]&/@Partition[Riffle[editedFiles,patchFiles],2]
G:\ Server \ home \ site \ www_patch \ administrator \ components \ com_component \ models \ bookings.php
G:\ Server \ home \ site \ www_patch \ administrator \ components \ com_component \ views \ bookings \ tmpl \ all.php
G:\ Server \ home \ site \ www_patch \ administrator \ components \ com_component \ views \ bookings \ tmpl \ default.php
G:\ Server \ home \ site \ www_patch \ administrator \ components \ com_component \ views \ bookings \ tmpl \ edit.php
G:\ Server \ home \ site \ www_patch \ components \ com_component \ models \ bookings.php
G:\ Server \ home \ site \ www_patch \ components \ com_component \ models \ item.php
G:\ Server \ home \ site \ www_patch \ components \ com_component \ models \ rooms.php
G:\ Server \ home \ site \ www_patch \ components \ com_component \ templates \ default \ emails \ email.booking.new.php
G:\ Server \ home \ site \ www_patch \ components \ com_component \ templates \ default \ item \ default.php
G:\ Server \ home \ site \ www_patch \ components \ com_component \ templates \ default \ reservation \ default.php
G:\ Server \ home \ site \ www_patch \ components \ com_component \ templates \ default \ reservation \ details.php
G:\ Server \ home \ site \ www_patch \ components \ com_component \ views \ reservation \ view.html.php
G:\ Server \ home \ site \ www_patch \ configuration.php
G:\ Server \ home \ site \ www_patch \.htaccess
G:\ Server \ home \ site \ www_patch \ tcpdf \ examples \ index.php

Все! Выделенные ранее файлы, которые были помечены нами - скопированы в новую папку, содержимое которой теперь можно залить в корень сайта по фтп или передать клиенту.

Копирование по метке (дополнение).

Экспериментирование в коде привело меня ко второй реализации того же кода. Отличия только третьим и четвертым шагом работы, и скоростью. Здесь уже искомый ключ задается напрямую строкой и поиск по файлам осуществляем через FindList:

searchData = "webengineer#28012014";
editedFiles = Cases[If[FindList[StringJoin[dirPath,#],searchData,1][NotEqual]{},StringJoin[dirPath,#],0]&/@filelist,Except[0]];

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

Копирование по дате последнего редактирования.

В этой реализации код практически полностью повторяет код представленный выше, отличия лишь в третьем и четвертом шаге. А именно вместо искомой строки, мы задаем ключевую дату:

keyDate= DateList[{2014,01,29,0,0,0}];

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

editedFiles = Cases[If[DateDifference[keyDate,FileDate[StringJoin[dirPath,#]],"Minute"][[1]]>0,StringJoin[dirPath,#],0]&/@filelist,Except[0]]

Далее все то же самое, как и в приведенном примере выше. На выходе снова получаем папку с файлами, при этом папка повторяет иерархическую структуру файловой системы обрабатываемого сайта.

Примеры Вы можете скачать по ссылке в чуть ниже.

Ссылки для скачивания.

Здесь Вы можете скачать сопроводительные материалы к статье:

examples.zip (3Kb)

* * * * * * * * * * * *

Если информация этой статьи будет интересна и полезна Вашему кругу друзей и знакомых, то Вы можете опубликовать ссылку - тогда им проще будет ее найти. Они Вам будут благодарны:).

Комментарии к статье:


Всеволод Чупрыгин © webengineer.pro 2014. Все права защищены.
Копирование материалов сайта разрешено только с указанием имени автора (Всеволод Чупрыгин) и прямой индексируемой ссылки на источник на сайте www.WebEngineer.pro.
ИП Чупрыгин Всеволод Андреевич, ИНН: 333410747832, ОГРН: 311333426300044
http://vkontakte.ru/chuprygin_va, Google +

.
Проверить аттестат
Мы принимаем Webmoney Мы принимаем практически все платежи через Robokassa Мы принимаем Яндекс.Деньги Мы принимаем платежи через QIWI. Мы принимаем платежи через привязанные к QIWI карты VISA/Mastercard.