Протокол File Transfer Protocol (Протокол передачи файлов), или FTP, применяется для пересылки файлов по сети между хостами. С использованием PHP можно вызывать fopen() и другие файловые функции для для FTP-соединений так же, как и для HTTP-соединений, для установки соединения и передачи файлов на FTP-сервер или обратно. Кроме того, в стандартной установке PHP имеется набор специализированных функций для работы с FTP.
Однако эти функции по умолчанию не инсталлируются. Для того чтобы воспользоваться ими под Unix, необходимо запустить конфигурационную PHP-программу confiqure с опцией --enable-ftp, а затем запустить перекомпиляцию с помощью make.
При использовании стандартной установки для Windows FTP-функции инсталлируются автоматически.
FTP-функции удобны для пересылки и копирования файлов с других хостов и на них. Один из распространенных примеров их использования — это создание резервной или зеркальной копии веб-сайта на другом сервере.
ftpmirror.php — сценарий для загрузки новых версий файла из FTP-сервера
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Обновление зеркальной копии</title>
</head>
<body>
<h1>Обновление зеркальной копии</h1>
<?php
//Установка переменных - измените их для своих целей
$host = 'ftp.cs.rmit.edu.au';
$user = 'anonymous';
$password = '[email protected]';
$remotefile = '/pub/tsg/teraterm/ttsshl.zip';
$localfile = 'tmp/writable/ttsshl.zip';
//Подключение к хосту
$conn = ftp_connect("$host");
if (!$conn)
{
echo 'Ошибка: соединение с FTP-сервером невозможно <br>';
exit;
}
echo "Установлено соединение с $host <br>";
//Регистрация на хосте
@ $result = ftp_login($conn, $user, $password);
if(!$result)
{
echo "Ошибка: пользователь $user не зарегистрирован <br>";
ftp_quit($conn);
exit;
}
echo "Начало сеанса пользователя $user <br>";
//Проверка времени модификации файла - следует ли его обновлять
echo "Проверка времени модификации файла ... <br>";
if (file_exists($localfile))
{
$localtime = filemtime($localfile);
echo 'Последняя модификация локального файла: ';
echo date('G:i j-M-Y', $localtime);
echo '<br>';
}
else
$localtime=0;
$remotetime = ftp_mdtm($conn, $remotefile);
if (!($remotetime >= 0))
{
//Это не значит, что файл не существует, сервер может не поддерживать время модификации
echo 'Невозможно получить время модификации удаленного файла. <br>';
$remotetime = $localtime+1; //чтобы обновление выполнялось
}
else
{
echo 'Последняя модификация файла: ';
echo date ('G:i j-M-Y', $remotetime);
echo '<br>';
}
if (!($remotetime > $localtime))
{
echo 'Локальная копия актуальна. <br>';
exit;
}
//Загрузка файла
echo 'Чтение файла с сервера ... <br>';
$fp = fopen ($localfile, 'w');
if (!$success = ftp_fget($conn, $fp, $remotefile, FTP_BINARY))
{
echo 'Ошибка: файл не может быть загружен';
ftp_quit($conn);
exit;
}
fclose ($fp);
echo 'Файл успешно загружен';
//Закрытие соединения с хостом
ftp_quit($conn);
?>
</body>
</html>
Результат исполнения сценария.
Сценарий ftpmirror.php является достаточно обобщенным. Он начинается с установки переменных:
$host = 'ftp.cs.rmit.edu.au';
$user = 'anonymous';
$password = '[email protected]';
$remotefile = '/pub/tsg/teraterm/ttsshl.zip';
$localfile = 'tmp/writable/ttsshl.zip';
Переменная $host должна содержать имя FTP-сервера, с которым будет происходить соединение, а переменные $user и $password соответствуют имени пользователя и паролю, необходимым для регистрации на сервере.
Многие FTP-сайты поддерживают так называемый анонимный вход (anonymous login), то есть общедоступное имя, которым может воспользоваться для регистрации любой пользователь. Пароль в этом случае не требуется, но правилом хорошего тона считается ввод в качестве пароля своего почтового адреса, чтобы системные администраторы могли знать местоположение своих пользователей. Здесь это соглашение соблюдено.
Переменная $remotefile содержит путь к файлу, который требуется загрузить. В данном случае происходит загрузка и создание зеркальной локальной копии программы Tera Term SSH — клиента для Windows.
Переменная $localfile содержит путь, по которому должен быть помещен загруженный файл на локальной машине. Для примера создан каталог /tmp/writable с правами доступа, достаточными для записи в него файла PHP-сценарием.
Независимо от используемой операционной системы, этот каталог потребуется явно создать. Если ваша операционная система поддерживает строгие соглашения по правам доступа, вы должны убедиться, что сценарий имеет права записи в этот каталог. Чтобы адаптировать сценарий под собственные нужды, необходимо будет соответствующим образом изменить значение этих переменных.
Основные шаги в этом сценарии совпадают с действиями, предпринимаемыми при ручной загрузке файла через FTP с использованием интерфейса командной строки:
Рассмотрим последовательно каждый шаг.
Этот шаг равносилен вводу.
ftp имя_хоста
в командной строке Windows или Unix. В PHP его можно выполнить с помощью следующего кода:
$conn = ftp_connect("$host");
if (!$conn)
{
echo 'Ошибка: соединение с FTP-сервером невозможно <br>';
exit;
}
echo "Установлено соединение с $host <br>";
Здесь использована функция ftp_connect(). Функция принимает в качестве параметра имя хоста и возвращает либо дескриптор соединения, либо значение false, если соединение не удалось. Вторым, необязательным, параметром функции является номер порта подключения на хосте. Здесь он не используется. Если номер порта не указан, по умолчанию соединение осуществляется через порт 21, стандартный FTP-порт.
Следующий шаг состоит в регистрации на сервере под именем определенного пользователя и указанием его пароля. Он достигается с помощью вызова функции ftp_login()^
@ $result = ftp_login($conn, $user, $password);
if(!$result)
{
echo "Ошибка: пользователь $user не зарегистрирован <br>";
ftp_quit($conn);
exit;
}
echo "Начало сеанса пользователя $user <br>";
Эта функция принимает три параметра: дескриптор FTP-соединения (возвращаемый функцией ftp_connect()), имя пользователя и пароль. При успешной регистрации пользователя функция возвращает true, а иначе — false. В начале первой строки находится символ @, который подавляет вывод сообщений об ошибках. Это делается для подавления предупреждений PHP в окне браузера в случае, если пользователь не сможет зарегистрироваться. Лучше перехватить ошибку, проверив значение переменной $result, как это сделано в рассматриваемом сценарии, и выдать свое, более дружественное сообщение.
Отметте, что если попытка регистрации завершилась неудачно, то фактически FTP-соединение закрывается функцией ftp_quit(), которая будет рассмотрена дальше.
Если требуется обновить локальную копию файла, то логично сначала проверить, следует ли обновлять файл, поскольку вовсе нет необходимости заново загружать файл, особенно большой, если он находится в актуальном состоянии. Это позволяет минимизировать сетевой трафик. Рассмотрим фрагмент кода, который решает эту задачу.
Учет времени создания и модификации файлов является причиной, по которой используются именно FTP-функции, а не более простые файловые функции. С помощью файловых функций можно просто читать, а в ряде случаев и записывать файлы через сетевые интерфейсы, однако большинство функций получения статуса файлов, например, filemtime(), не работает с удаленными файлами. Наверняка в будущем ситуация может измениться.
Вначале для определения необходимости загрузки файла с помощью функции file_exists() проверяется наличие локальной копии файла. Если копии нет, то ясно, что файл необходимо загрузить. Если же она существует, то при помощи функции filemtime() запрашивается время последней модификации файла, которое присваивается переменной $localtime. Если локальной копии файла нет, переменной $localtime присваивается значение 0, чтобы сделать локальный файл заведомо "старше" любой даты модификации удаленного файла:
echo "Проверка времени модификации файла ... <br>";
if (file_exists($localfile))
{
$localtime = filemtime($localfile);
echo 'Последняя модификация локального файла: ';
echo date('G:i j-M-Y', $localtime);
echo '<br>';
}
else
$localtime=0;
После выделения времени модификации локального файла необходимо узнать время модификации файла на удаленной машине. Это можно сделать с помощью функции ftp_mdtm():
$remotetime = ftp_mdtm($conn, $remotefile);
Эта функция принимает два параметра — дескриптор FTP-соединения и путь к удаленному файлу — и возвращает либо метку времени последней модификации файла в формате Unix, либо -1, если произошла какая-либо ошибка. Не все FTP-серверы обеспечивают такую возможность, поэтому вызов этой функции не всегда дает требуемый результат. В этом случае в переменную $remotetime исскуственно заносится "более новое" время, чем в $localtime, то есть увеличенное на 1. Это гарантирует, что будет предпринята попытка загрузить файл:
if (!($remotetime >= 0))
{
//Это не значит, что файл не существует, сервер может не поддерживать время модификации
echo 'Невозможно получить время модификации удаленного файла. <br>';
$remotetime = $localtime+1; //чтобы обновление выполнялось
}
else
{
echo 'Последняя модификация файла: ';
echo date ('G:i j-M-Y', $remotetime);
echo '<br>';
}
Теперь, когда имеются даты двух файлов, их можно сравнить и сделать вывод о необходимости загрузки файла:
if (!($remotetime > $localtime))
{
echo 'Локальная копия актуальна. <br>';
exit;
}
На этой стадии происходит попытка загрузить файл из сервера:
echo 'Чтение файла с сервера ... <br>';
$fp = fopen ($localfile, 'w');
if (!$success = ftp_fget($conn, $fp, $remotefile, FTP_BINARY))
{
echo 'Ошибка: файл не может быть загружен';
ftp_quit($conn);
exit;
}
fclose ($fp);
echo 'Файл успешно загружен';
Как уже должно быть известно, локальный файл открывается с помощью функции fopen(). После чего вызывается функция ftp_fget(), которая загружает файл и сохраняет его на локальном диске. Эта функция принимает четыре параметра. Назначение первых трех из них очевидно: дескриптор FTP-соединения, дескриптор локального файла и путь к удаленному файлу. Четвертый параметр определяет FTP-режим.
Пересылка через FTP может осуществляться в двух режимах: ASCII и бинарном. Режим ASCII используется для пересылки текстовых файлов (то есть файлов, состоящих только из ASCII-символов), а бинарный режим — для пересылки всех остальных файлов. В бинарном режиме файл передается без какой-либо модификации, тогда как в режиме ASCII выполняется трансляция символов возврата каретки и перевода строки в соответствующие сиволы, используемые в вашей системе (\n для Unix, \r\n для Windows и \r для Macintosh).
PHP-библиотека для работы с FTP содержит две предопределенных константы — FTP_ASCII и FTP_BINARY, соответствующие этим двум режимам. Необходимо выяснить, какой режим более соответствует типу файла и передать соответствующую константу в четвертом параметре функции ftp_fget(). В данном случае пересылается zip-файл, поэтому используется режим FTP_BINARY.
Функция ftp_fget() возвращает значение true, если копирование произошло успешно, и false, если возникла ошибка. Результат сохраняется в переменной $success; он позволит сообщить пользователю, как прошла загрузка.
После загрузки локальный файл закрывается с помощью функции fclose().
Вместо ftp_fget() можно было бы воспользоваться альтернативной функцией —ftp_get(), которая имеет следующий прототип:
int ftp_get (int ftp_connection, string localfile_path, string remotefile_path, int mode)
Эта функция работает практически так же, как ftp_fget(), но не требует открытия локального файла. Ей передается имя локального файла, в который будет выполняться запись, а не его дескриптор.
Обратите внимание, что в PHP не существует эквивалента FTP-команды mget, посредством которой можно загрузить сразу несколько файлов. Вместо этого нужно несколько раз вызвать ftp_fget или ftp_get.
По окончании необходимо закрыть FTP-соединение с помощь функции ftp_quit():
ftp_quit($conn);
Этой функции должен быть передан дескриптор FTP-соединения.
Если же, наоборот, требуется скопировать файлы с локального сервера на удаленную машину, для этого используются две функции, противоположные ftp_fget() и ftp_get(). Они называются ftp_fput() и ftp_put() и имеют следующие прототипы:
int ftp_fput (int ftp_connection, string remotefile_path, int fp, int mode)
int ftp_put (int ftp_connection, string remotefile_path, string localfile_path, int mode)
Их аргументы совпадают с аргументами своих _get-эквивалентов.
При передаче файлов через FTP можно столкнуться с проблемой превышения максимального времени выполнения сценария. Если это происходит PHP выдает сообщение об ошибке. Так часто бывает, если локальный сервер подключен к медленной или перегруженной сети, или когда загружается большой файл, например, видеоклип.
Максимальное время выполнения по умолчанию для всех сценариев PHP определено в файле php.ini. По умолчанию оно составляет 30 секунд. Это сделано для автоматического прекращения выполнения сценариев, вышедших из под контроля. Однако при пересылке файлов через FTP, если у вас медленная связь или очень большой файл, пересылка может занять больше максимально разрешенного времени.
К счастью, с помощью функции set_time_limit() можно изменять максимальное время выполнения конкретного сценария. Вызвав эту функцию, можно установить максимальное время для выполнения сценария (в секундах), начиная с момента вызова функции. Например:
set_time_limit(90);
позволит сценарию выполняться еще 90 секунд с момента вызова функции.
В PHP существует множество других полезных функций работы с FTP.
Функция ftp_size() возвращает размер файла на удаленном сервере и имеет следующий прототип:
int ftp_size (int ftp_connection, string remotefile_path)
Она возвращает размер удаленного файла в байтах или значение -1 в случае возникновения ошибки. Следует отметить, что не все FTP-серверы поддерживают эту функцию.
С помощью функции ftp_size() легко оценить максимальное время выполнения пересылки какого-либо файла. Зная размер файла и скорость соединения, можно приблизительно оценить время передачи и соответствующим образом воспользоваться функцией set_time_limit().
Список файлов в каталоге на удаленном FTP-сервере можно получить с помощью следующего кода:
$listing = ftp_nlist ($conn, dirname ($remotefile));
foreach ($listing as $filename)
echo "$filename <br>";
Здесь для получения списка имен файлов в конкретном каталоге применяется функция ftp_nlist().
В PHP доступны и другие FTP-функции, которые позволяют выполнить практически все, что можно сделать в командной строке FTP-клиента. FTP-функции, соответствующие командам FTP, можно найти в онлайновом руководстве по PHP, которое доступно по адресу http://php.net/manual/en/ref.ftp.php
Исключение составляет команда mget (множественная загрузка), однако можно воспользоваться функцией ftp_nlist() для получения списка файлов, а затем поочередно загрузить их.
Похожие материалы по теме: Работа с FTP-сервером. Установка соединения с FTP-сервером. Навигация по FTP-серверу