программирование

Сегодня хочу рассказать Вам как можно загрузить каталог файлов на FTP с помощью PHP. Задача не тривиальна тем, что кроме непосредственной загрузки файла нужно обходить весь каталог рекурсивной функцией и загружать все файлы (т.к. списка файлов в каталоге изначально нет).

Где может использоваться такой скрипт? Я написал его себе, т.к. мне надоело постоянно вручную загружать один и тот же движок на разные сайты (сателлиты), гораздо легче указать 20 доменов скрипту и уйти пить чай, а не сидеть перед компьютером. Или нет? O_o

Чтобы PHP научился работать с FTP нужно:
Для Linux: при инсталляции указать флаг: –enable-ftp
Для Windows: поддержка работы с FTP устанавливается по умолчанию.

Следующий шаг, который придется сделать, это отключить лимит времени выполнения скрипта. Понятно, что файлов для загрузки может быть много или даже очень много, поэтому нельзя ограничивать скрипт стандартными 30 секундами работы. Напоминаю, что отключается это в php.ini в строке max_execution_time, ее нужно установить равной нулю. Можно, конечно, воспользоваться функцией set_time_limit (0), но я предпочитаю ее не использовать.

Теперь переходим к созданию самого скрипта. Скрипт будет состоять из двух больших частей – функции обхода каталога и подключения и загрузки на FTP. Наверняка скрипт можно оптимизировать, можно вообще поменять разные куски кода – я, опять же, писал так, как мне удобнее его использовать.

Шаг 1: Пишем скрипт обхода каталога.

Ну, то есть как пишем… Никак не пишем. Вот он:


//Загружает файлы
function my_upload($new_dir){
	$files = glob("*");
	foreach ($files as $file){
		if ($file == "." || $file == "..") continue;

		echo "$file, "; flush();

		if (is_dir ($file)){
			chdir ($file);
			my_upload ($file);
			chdir ("..");
		}
	}
}

Как функция работает? Сначала получаем список всех файлов в данной категории в массив $files с помощью функции glob(). Эта функция на самом деле очень клевая, я недавно о ней узнал и очень рад. Теперь всякие там opendir, readdir и прочее отдыхают.

Разбираем этот массив по элементам ($file, там содержится имя очередного файла или категории): если это точка или двоеточие (что означает родительскую категорию и переход на уровень вверх соответственно) – сразу пропускаем этот шаг цикла (т.к. это не файлы и работать с ними не получится). Дальше выводим имя текущего файла в браузер для наглядности работы (позже здесь будет загрузка на FTP). Если не использовать функцию flush(), то возникнет эффект зависания.

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

Конечно, после того, как мы ушли на каталог вниз и вызвали функцию рекурсивно, нам нужно перейти обратно, чтобы читать файлы дальше (это к слову о вызове chdir («..»);).

Использовать эту функцию придется следующим образом (подобно тому, как она использует сама себя при рекурсии):

chdir ("dir_to_upload");
@my_upload();
echo "ок.";

То есть переходим в каталог (относительно места расположения скрипта), который нам нужно загрузить на FTP и вызываем функцию загрузки.

Шаг 2, Встраиваем загрузку на FTP.

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

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


$connect = ftp_connect ($host);
if (!$connect)
	die ("Не удалось установить соединение.");

if (!ftp_login ($connect, $user, $password))
	die ("Не подключились.");

// ftp_chdir ($connect, $directory);

chdir ("dir_to_upload");
@my_upload();

if ($connect) ftp_quit ($connect);

Имена функций прекрасно переводятся с английского, поэтому комментировать даже не хочется. Уточню, что закомментрированная строчка ftp_chdir нужна, если Вы хотите загрузить файлы в определенный каталог ($directory там как раз и есть имя каталога). Вызов функции загрузки прокомментирован выше.

Так… Да, еще нужно опять поменять функцию обхода, добавив в нее загрузку файлов. Теперь она выглядит так:


global $connect;

$files = glob("*");
foreach ($files as $file){
	if ($file == "." || $file == "..") continue;

	echo "| "; flush();

	if (is_file ($file)){
		ftp_put ($connect, $file, $file, FTP_BINARY);
	}

	if (is_dir ($file)){
		ftp_mkdir ($connect, $file);
		chdir ($file);
		ftp_chdir ($connect, $file);
		my_upload ($file);
		ftp_chdir ($connect, "..");
		chdir ("..");
	}
}

Комментарии по поводу новых строчек кода. Делаем глобальным дескриптор подключения к ftp ($connect), нам ведь нужно использовать уже установленное поделючение (если Вы оформили загрузку не в виде функции, то это не нужно). Опять, как обычно, читаем файлы. Но теперь, вместо вывода файла мы его загружаем. Проверка, файл ли это, осуществляется функцией is_file(), и если файл – загружаем его с помощью функции ftp_put (первый параметр – соединение, второй – конечное имя файла, третий – локальное имя файла, четвертый – тип загрузки). По идее для передачи изображений нужно использовать FTP_BINARY, а для передачи текста FTP_ASCII, но у меня все нормально работает и просто только при FTP_BINARY O_o

Ну и, если нашли каталог, то создаем его на FTP с помощью функции ftp_mkdir(), переходим в него, заливаем файлы, выходим из него. Дальше все без изменений.

Эпилог. Загрузить каталог средствами PHP по FTP у меня не вышло, пришлось писать рекурсивную функцию (что я здесь Вам и представил). Вполне может быть, что я пропустил какую-то функцию, которая сама все это делает и не нужно писать огромные километры кода, но не нужно тыкать в меня пальцем и смеяться, т.к. я все равно считаю, что мой способ лучше, т.к.:

1) развивает голову (самый важный орган у программиста, после попы и пальцев, конечно);
2) здесь у меня в нее встроен индикатор загрузки (вывод вертикальной палки по мере обхода цикла как символ того, что все работает нормально и загрузка идет; одна палочка – один файл загружен);
3) можно встроить дополнительные бонусы, например, возможно пропускать файлы с определенным расширением, с определенными именами и прочее и прочее.

Я отметил интересные функции ссылками на их описания. Но очень рекомендую при непонятках с другими функциями не игнорировать «великий источник знаний», а все же пойти в мануал. :) Если что непонятно, как всегда – на все вопросы постараюсь ответить, пишите в комментариях.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *