Статьи

RSS-каналы для FTP-серверов

  1. RSS-каналы для FTP-серверов 22 марта 2006 г. Марк Вудман Приложения для RSS распространились далеко за пределы способа распространения новостей. RSS теперь используется для всего от пакеты отслеживания в автомобильные дилерские запасы , Они отражают один из главных аспектов RSS: вы можете использовать его, чтобы сообщать вам, когда что-то происходит, что вас волнует, вместо того, чтобы проверять себя. В этом духе, эта статья покажет вам, как написать PHP-скрипт, который будет контролировать FTP-сервер для вас, уведомляя вас о последних добавленных или измененных файлах. PHP, FTP и ты
  2. Знать код
  3. Функция exploreFtpServer ()
  4. Функция scanDirectory ()
  5. Функция generateRssFeed ()
  6. Положи это на работу
  7. Повысьте производительность
  8. Отдай это обратно

RSS-каналы для FTP-серверов

22 марта 2006 г.

Марк Вудман


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

PHP, FTP и ты

Со всем акцентом на веб-функциональность, команды FTP в PHP часто упускаются из виду. Хорошей новостью является то, что эти функции включены в стандартный PHP 4, поэтому внешние библиотеки не требуются.

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

<? php phpinfo (); ?>

При просмотре вышеуказанного скрипта в веб-браузере вы увидите классическую страницу PHP Info с почти всей информацией о конфигурации, которая вам когда-либо понадобится. Прокрутите вниз до раздела FTP, чтобы увидеть, была ли включена «Поддержка FTP». Это должно выглядеть примерно так:

Рисунок 1
Рисунок 1. PHP Info, показывающий, что FTP включен

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

Это много для поглощения, но Руководство по PHP по функциям FTP полезная ссылка Возможно, вы захотите держать его под рукой во время прохождения этого урока. (А если у вас бессонница, вы всегда можете попробовать прочитать RFC 959 , спецификация для самого FTP.)

Знать код

Для этого урока мы создадим скрипт PHP с именем ftp_monitor.php . Мы рассмотрим скрипт по частям, но вы также можете скачать полный исходный код для справки.

Функция exploreFtpServer ()

Давайте начнем с сути функций FTP, инкапсулированных в функцию exploreFtpServer (). Это будет принимать параметры для имени хоста FTP, имени пользователя, пароля и начального пути. Цель этой функции - изучить FTP-сервер, пройтись по любым каталогам, а затем вернуть ассоциативный массив имен файлов и соответствующие временные метки файлов.

После объявления сигнатуры функции используйте PHP ftp_connect () попытаться подключиться к FTP-серверу. Если соединение создано, мы сохраним идентификатор соединения в переменной $ cid для использования со всеми функциями PHP, связанными с FTP.

function exploreFtpServer ($ host, $ user, $ pass, $ path) {// Connect $ cid = ftp_connect ($ host) или die («Не удалось подключиться к серверу»);

Для простоты приведенный выше код в итоге остановит выполнение скрипта, если не удастся установить FTP-соединение. После того, как вы некоторое время использовали этот скрипт, вы можете добавить более надежную обработку ошибок.

Как только у нас будет соединение, мы попытаемся пройти аутентификацию на сервере с помощью функции PHP ftp_login () , Как и большинство FTP-функций PHP, первым аргументом является идентификатор соединения ($ cid). Этот конкретный также принимает имя пользователя и пароль.

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

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

Наконец, работает ли аутентификация или нет, нам нужно закрыть соединение с сервером, используя ftp_close () , Опять же, этот пример остановит скрипт с умереть() если аутентификация не удалась, но вы можете решить эту проблему другим способом.

// Войти в систему if (ftp_login ($ cid, $ user, $ pass)) {// Пассивный режим ftp_pasv ($ cid, true); // Рекурсивная структура каталогов $ fileList = scanDirectory ($ cid, $ path); // Отключаем ftp_close ($ cid); } else {// Отключаем ftp_close ($ cid); die («Не удалось подтвердить подлинность».); }

На данный момент у нас есть заполненная переменная $ fileList, которая является ассоциативным массивом. Ключи - это имена файлов, а значения - временные метки файлов. Этот массив будет наиболее полезен, если он отсортирован по отметке времени - сначала новее, поэтому сортируйте его с помощью arsort () и верни это.

// Сортировка по отметке времени, самое новое (наибольшее число) first arsort ($ fileList); // Вернуть результат return $ fileList; }

Функция scanDirectory ()

Теперь мы готовы написать функцию scanDirectory (), которая вызывается из описанного выше нашего exploreFtpServer (). Цель этой функции - сканировать каталог FTP на наличие файлов и подкаталогов, добавляя первый в список и повторяя его через последний. Параметры для передачи - это идентификатор FTP-соединения ($ cid) и начальный каталог ($ dir). Мы также объявим статическую переменную $ fileList, которая будет использоваться для сохранения нашего списка файлов при рекурсивных вызовах функции.

Чтобы получить содержимое данного каталога в FTP, мы будем использовать ftp_nlist () функция. К сожалению, эта функция не идеальна. На большинстве FTP-серверов, которые я тестировал, он возвращает список имен файлов и каталогов. Но есть несколько, как WU-FTPD , который только возвращает список имен файлов. На таких серверах наш скрипт может отслеживать только начальный каталог, который указан; никакие подкаталоги не будут отслеживаться.

Альтернативой ftp_nlist () является ftp_rawlist () , который должен предоставлять все содержимое каталога независимо от его типа. К сожалению, формат данных, возвращаемых функцией ftp_rawlist (), по-видимому, не стандартизирован, поэтому любая попытка создать «универсальный синтаксический анализатор» является непростой задачей. (Читайте комментарии пользователей на ftp_rawlist () чтобы понять, что я имею в виду.) Таким образом, ради учебника мы будем придерживаться несовершенного, но гораздо более простого метода ftp_nlist ().

function scanDirectory ($ cid, $ dir) {// Использовать статическое значение для сбора результатов static $ fileList = array (); // Получить список содержимого каталога $ contents = ftp_nlist ($ cid, $ dir);

Переменная $ contents теперь заполняется простым массивом имен файлов и каталогов. В зависимости от сервера эти элементы могут содержать или не содержать путь к самому файлу. (Мы можем получить «foo.txt» или «/i/pity/the/foo.txt».)

Этот следующий раздел scanDirectory () будет перебирать каждое имя и использовать ftp_size () определить, является ли имя файла или каталога. (Это дешевый трюк: каталоги возвращают размер -1.) Если элемент является файлом, мы добавим косую черту, если необходимо, чтобы наши пути были согласованными, затем используйте ftp_mdtm () функция, чтобы получить его модификацию timestamp. Затем мы добавим имя файла в качестве ключа в наш ассоциативный массив $ fileList и используем его метку времени в качестве значения:

// Итерация по содержимому каталога if ($ contents! = Null) {foreach ($ contents as $ item) {// Является ли элемент файлом? if (ftp_size ($ cid, $ item)> = 0) {// Добавить косую черту, если ее нет, если ($ item [0]! = "/") $ item = "/". $ Пункт; // Добавить файл и изменить временную метку к результатам $ fileList [$ item] = ftp_mdtm ($ cid, $ item); }

Теперь нам нужно разобраться с элементом, возвращаемым ftp_nlist (), который является каталогом. Мы обязательно проигнорируем псевдонимы того же или родительского каталога. Если у нас есть подходящее имя каталога, мы можем вызвать scanDirectory (), чтобы вернуться в него. (Это требует некоторой дополнительной логики для обработки вариаций между серверами, которые используют полный или относительный путь.) С обработанными файлами и каталогами мы можем вернуть $ fileList, содержащий каждый найденный файл. Вот как это все выглядит:

else // Item - это каталог {// Исключить собственные / родительские псевдонимы if ($ item! = "." && $ item! = ".." && $ item! = "/") {// Сервер использует полные пути if ($ item == strstr ($ item, $ dir)) {scanDirectory ($ cid, $ item); } else {// Сервер использует относительные имена путей if ($ dir == "/") {scanDirectory ($ cid, $ dir. $ item); } else {scanDirectory ($ cid, $ dir. "/". $ item); }}}}}} // Возвращаем результаты return $ fileList; }

Функция generateRssFeed ()

Это стало похоже на учебник по PHP, не так ли? Хорошая новость: сложная часть закончена, и теперь осталось только написать функцию, которая на самом деле генерирует RSS-канал.

Эту функцию мы будем вызывать напрямую из другого скрипта PHP, передавая все параметры, необходимые для ее работы:

  • $ host: имя хоста FTP-сервера. Пример: "ftp.foo.com".
  • $ user: имя пользователя FTP. Пример: "аноним".
  • $ pass: пароль пользователя FTP. Пример: "гость".
  • $ path: начальный каталог на сервере. Пример: "/ pub / crawl"
  • $ itemCount: количество элементов для возврата в RSS-канал.

Первое, что мы сделаем, это вызовем нашу функцию exploreFtpServer (), чтобы получить список файлов и их временные метки с FTP-сервера. Как только список возвращен, мы можем видеть, короче ли список, чем переданный $ itemCount, и использовать меньшее число из двух:

function generateRssFeed ($ host, $ user, $ pass, $ path, $ itemCount) {// Получить массив файловых / временных массивов $ fileList = exploreFtpServer ($ host, $ user, $ pass, $ path); // Используем количество пользователей или количество найденных файлов, в зависимости от того, что меньше if (count ($ fileList) <$ itemCount) $ itemCount = count ($ fileList);

У нас есть несколько переменных, которые нужно объявить, прежде чем продолжить. Первый - это $ linkPrefix для хранения имени хоста с префиксом протокола FTP. Следующим является $ channelPubDate, который будет содержать дату публикации RSS-канала. Мы также создадим массив $ items для хранения элементов RSS, которые мы создаем.

// Создать префикс ссылки для фида $ linkPrefix = 'ftp: //'. $ Хост; // Объявляем дату для канала / pubDate $ channelPubDate = null; // Массив для строк элементов $ items = array ();

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

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

// Создать массив элементов RSS из самых последних файлов foreach ($ fileList as $ filePath => $ time) {// Создать item / pubDate в соответствии с RFC822 $ itemPubDate = date ("r", $ time); // Также используем первый item / pubDate в качестве channel / pubDate if ($ channelPubDate == null) $ channelPubDate = $ itemPubDate;

Далее мы создадим URI файла, начиная с «ftp: //». Эта переменная $ fileUri будет использоваться для заполнения как заголовка элемента RSS, так и ссылки. Мы должны заменить все пробелы в именах файлов на закодированное значение «% 20», чтобы убедиться, что URI правильно сформирован.

Теперь, когда у нас есть вся необходимая информация, пришло время создать XML для каждого элемента RSS. Когда это будет сделано, мы добавим его в наш массив $ items для дальнейшего использования. Мы также обязательно завершим этот цикл, если достигнем порога $ itemCount.

// Создать URI для ftp file $ fileUri = ereg_replace ("", "% 20", $ linkPrefix. $ FilePath); // Создать элемент $ item = '<item>'. '<Title>'. $ fileUri. '</ title>'. '<link>'. $ fileUri. '</ link>'. '<pubDate>'. $ itemPubDate. '</ pubDate>'. '</ item>'; // Добавить в массив элементов array_push ($ items, $ item); // Если достигнуто максимальное количество элементов для фида, остановите if (count ($ items) == $ itemCount) break; }

Наконец, мы можем создать сам канал RSS. Построить XML в PHP с использованием строк легко, но редко на что приятно смотреть. Обратите внимание, что мы используем join () для добавления всех наших элементов RSS, с разрывом строки после каждого. (Разрывы строки не обязательны, но они облегчают чтение канала при устранении неполадок.)

// Создаем RSS-канал $ rss = '<rss version = "2.0">'. <канал>. <title> FTP Monitor: '. $ host. </ title>. <ссылка>. $ linkPrefix. </ link>. <описание>. $ ITEMCOUNT. последние изменения на. $ host. $ path. '(из'. count ($ fileList). 'files) </ description>'. '<pubDate>'. $ channelPubDate. </ pubDate> '. "\ n". объединение ("\ n", $ items) . "\ n". </ channel>. </ Новости> ';

Корм готов к работе. Осталось только установить HTTP-заголовок, чтобы указать, что мы возвращаем XML-документ, а затем вывести фид:

// Установить заголовок для XML-заголовка MIME-типа ("Content-type: text / xml; charset = UTF-8"); // Отображение RSS-канала echo ($ rss); }

И это последняя из настоящей работы. Если вы еще этого не сделали, обязательно загрузите полный исходный код ftp_monitor.php чтобы увидеть все это в одном месте.

Положи это на работу

После размещения ftp_monitor.php на вашем веб-сервере с поддержкой PHP вы можете ссылаться на него из любого другого сценария PHP. Вот пример того, как это может выглядеть:

<? php // Импортируем монитор FTP require_once ('ftp_monitor.php'); // Параметры соединения для мониторинга снимков FreeBSD $ host = "ftp.freebsd.org"; $ user = "anonymous"; $ pass = "[email protected]"; $ path = "/ pub / FreeBSD / snapshots"; // Создать ленту RSS 2.0, показывающую новейшие снимки FreeBSD generateRssFeed ($ host, $ user, $ pass, $ path, 10); ?>

Вот пример выходного файла из вышеуказанных параметров подключения: freebsd.xml , При просмотре в SharpReader , элементы выглядят так:

Рисунок 2
Рисунок 2. Элементы FTP-монитора для ftp.freebsd.org

Многие агрегаторы RSS будут автоматически переходить по ссылке элемента, если у него нет элемента описания. SharpReader является одним из этих агрегаторов и поддерживает протокол ftp: // . Таким образом, нажав на один из элементов с нашего FTP-монитора, вы начнете загружать его автоматически. Обычно это работает нормально, если FTP-сервер разрешает анонимные подключения. Однако если вам нужно было указать реальное имя пользователя и пароль в ftp_monitor.php , ваша способность «щелкать и скачивать» будет зависеть от того, сможет ли ваш читатель RSS запросить учетные данные FTP.

Повысьте производительность

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

Во-вторых, этот сценарий не предназначен для одновременного использования многими пользователями. Проблема скорости - это один из факторов, а другой - FTP-соединения. При каждом одновременном обращении к этому сценарию устанавливается соединение с FTP-сервером. Максимальное количество доступных подключений не займет много времени. Таким образом, если вы хотите предоставить RSS-канал большому количеству пользователей, вы должны скрыть этот скрипт за кешем, который периодически вызывает его. Пусть реальная загрузка будет обрабатываться вашим кешем, а не скриптом ftp_monitor .

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

Отдай это обратно

Если вы посчитаете этот скрипт полезным или если вы придумаете классную модификацию, я бы хотел услышать ваше мнение. Оставьте комментарий и поделитесь тем, что вы узнали, с сообществом xml.com.

2011.11.19
Карта