Глава 7, хостинг php файлов.

Хостинг php файлов

Глава 7. PHP: закачка файлов

Обычно в создании сайта ведущая роль принадлежит его web-мастеру. Посетителям остается лишь возможность просматривать сайт (для чего он, собственно, и делается) и присылать его админист-ратору свои пожелания по улучшению. Ну и, иногда, — свои материа-лы для размещения на сайте.

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

Но есть третий выход из положения. Следует выделить под статьи товарища отдельную папку и позволить ему загружать свои материа-лы туда и только туда. Как же это сделать? Прибегнуть к помощи PHP.

Для начала следует создать папку, куда будут помещаться закачи-ваемые посетителями файлы — скажем, user — в том примере, что бу-дет рассматриваться ниже, и присвоить ей атрибут 772, что означает предоставление посетителям сайта возможность записывать в нее файлы. Для присвоения атрибута достаточно, зайдя с помощью, ска-жем, СuteFTP на аккаунт, выбрать из контекстного меню этой папки (т.е. из меню правой кнопки мыши) пункт CHMOD (рис.7.1) и ука-зать, что виду пользователей Public разрешается в эту папку что-либо записывать (рис.7.2).

Рис.7.1. Не забудьте назначить папке для файлов нужные права доступа.

Рис.7.2. Например, вот так.

В других программах для работы по протоколу FTP права доступа настраиваются примерно аналогично.

Чтобы пользователь мог загрузить файл на сервер с web-страницы, на этой странице должна присутствовать форма с параметром заголовка enctype, равном «multipart/form-data», а также со спе-циальным полем типа file (выглядит как поле ввода имени файла с кнопкой «Обзор», нажав на которую, можно отобразить окно выбора файла) и кнопкой submit (см. например, на рис.7.3). Как только эта кнопка будет нажата, браузер начнет передавать файл, указанный в поле типа file, на сервер. В заголовке формы также следует указать параметр action, значением которого должно быть имя страницы с обрабатывающим загруженный файл сценарием.

Загрузку файла на сервер умеют осуществлять практически все браузеры (только самые старые модели Microsoft Internet Explorer и Netscape Navigator этого не могут), а воспринять ее могут все web-серверы (кроме CERN и некоторых самых простых), в том числе и самый распространенный — Apache.

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

Странице, указанной в параметре action заголовка формы, передаются несколько переменных (Если в конфигурационном файле PHP — php.ini — параметр register_globals ус-тановлен в on), содержащих информацию о загру-женном файле. Именно на их основе сценарий на ней сможет рабо-тать с загруженным файлом. Кроме того, эти же самые переменные помещаются в массив $HTTP_POST_FILES (Если в конфигурационном файле PHP — php.ini — параметр track_vars установлен в on), а в PHP версий 4.1 и выше — и в массив $_FILES (в отличие от переменных и массива $HTTP_POST_FILES этот массив по умолчанию доступен и во всех функциях, размещенных на странице с программой-обработчиком загруженных файлов).

    Вот эти переменные:
  1. 1. Переменная, имеющая то же имя, что и поле с типом file в ис-ходной форме. Скажем, если оно выглядело как <input name=»uploadfile» type=»file»>, то переменная будет иметь имя $up-loadfile (и, соответственно, еще $HTTP_POST_FILES[‘uploadfile’][‘tmp_name’], $_FILES[‘uploadfile’][‘tmp_name’]). В эту переменную записывается то имя (временное, создающееся автоматически), которое загруженный файл имеет в папке временных файлов. Именно с ним будут работать команды копирования файла.

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

  • 2. Переменная, имеющая имя «Переменная 1_name» — т.е. к имени первой переменной просто приписывается горизонтальная черточка и слово name, например, для вышеуказанного примера ее имя будет выглядеть как $uploadfile_name (ну и, разумеется, то же самое значе-ние получат элементы вышеупомянутых массивов $HTTP_POST_FILES[‘uploadfile’][‘name’], $_FILES[‘uploadfile’][‘name’]). Ее значением является исходное имя файла в системе отправителя.
  • 3. Переменная, имеющая имя «Переменная 1_size». Ее значение — размер загруженного файла в байтах.
  • 4. Переменная, имеющая имя «Переменная 1_type». Ее значение — тип загруженного файла согласно спецификации MIME (например, «image/gif»).
  • Все эти переменные можно использовать в PHP-сценарии, распо-ложенном на указанной в параметре action заголовка передающей файл формы странице. Для копирования файла используется команда copy («имя копируемого файла (и путь к нему)», «путь к папке, в ко-торую нужно файл скопировать и его новое имя там»). Путь к файлу во временной папке можно не указывать (она используется по умол-чанию), а путь к папке, куда файл должен быть помещен, должен указываться относительный от того каталога, в котором находится страница с обрабатывающим загруженный файл сценарием.

    Об удалении файла из временной папки после копирования его в нужный каталог можно не думать — это, как уже было сказано, про-изойдет автоматически.

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

    На странице, с которой должна производиться закачка файлов, следует поставить форму (рис. 7.3):

    Рис.7.3. Форма ввода имени файла для загрузки и пароля.

    (То есть программа, обрабатывающая загруженный файл, будет помещаться в файле up.php.)

    Теперь — поле ввода пароля. Его значение при передаче формы за-пишется в переменную с тем же именем, что и у этого поля (в данном случае — в переменную $pass), а также будет доступно в массиве $HTTP_POST_VARS, в элементе $HTTP_POST_VARS[‘pass’] (начи-ная с PHP версии 4.1 — и в элементе $_POST[‘pass’]). Ее вы сможете использовать в сценарии на странице, указанной в параметре action заголовка формы. Если вы укажете в параметре type этого поля зна-чение «text», то вводимые пользователем символы будут отображать-ся на экране, если «password» — то заместятся звездочками (как на рис.7.3).

    И поле ввода имени файла:

    Кнопка начала загрузки:

    Собственно, и все. Для загрузки достаточно.

    Теперь не менее важная часть — сценарий обработки загруженного файла.

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

    Если в поле ввода имени файла ничего не было, то выполнение сценария прекращаем (с выводом сообщения пользователю — напри-мер, как на рис.7.4), если же нет — то выполняется следующая за дан-ным условием команда elseif:

    Рис.7.4. Сообщение пользователю

    в случае неуказания имени файла.

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

    и введена в PHP для упрощения синтаксиса программ.

    Если введенный пользователем пароль не соответствует тому, что указан в данном сценарии (обратите внимание, что правильный па-роль для загрузки файлов указывается именно здесь!), то выполнение сценария прекращаем (с выводом сообщения пользователю — как на рис.7.5), если же нет — то выполняется следующая за данным услови-ем команда elseif.

    Рис.7.5. Сообщение пользователю в случае неправильного ввода пароля.

    Если вы желаете назначить разным пользователям отдельные па-роли (чтобы, скажем, иметь возможность запретить загружать файлы лишь одному из них, не затронув интересы остальных), то просто укажите здесь соответствующее условие (может выглядеть так: «elseif (($pass!=»parol1″)&&($pass!=»parol2″))»):

    И, наконец, само копирование — допустим, в папку user. По его окончании пользователю выдается соответствующее сообщение (рис.7.6).

    Рис.7.6. Сообщение пользователю в случае успешной загрузки.

    В условии elseif в нижеследующей строчке проверяется, выдает ли команда copy значение True — это происходит при успешном копиро-вании. (Если не можете разобраться в синтаксисе — смотрите Описа-ние PHP.)

    Команда copy выполняет копирование файла из того места, которое указано в ее первом параметре, на то, которое указано в втором. При наличии в месте назна-чения файла с тем же именем, что и у копируемого, новый файл пишется поверх старого. Команда возвращает True, если копирование проходит удачно, и False, если нет, выводя в последнем случае также сообщение на ту страницу, в сценарии на которой она расположена. (Если последнее нежелательно, то заблокируйте вы-вод сообщений об ошибке, поставив перед командой знак @.)

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

    Рис.7.7. Сообщение пользователю в случае ошибки копирования.

    Вот, собственно, и все. Пользователь, зайдя на первую страницу (рис.7.3), должен ввести свой пароль и найти с помощью открываю-щегося при нажатии на кнопку «Обзор» диалогового окна файл для загрузки. После нажатия кнопки «Загрузить» он увидит одно из трех сообщений — см.рис.7.4, 7.5, 7.6.

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

    Обратите внимание, что при формировании конечного имени файла (в команде copy), а также в строках, выводимых командой echo, писать просто имена элементов массива нельзя — будет выда-ваться ошибка! Необходимо использовать оператор конкатенации — точку:

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

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

    В результате в программу-обработчик будут переданы:

    1. Массивы $uploadfile[], $uploadfile_name[], $uploadfile_size[], $uploadfile_type[], содержащие соответственно временные имена за-груженных файлов, исходные имена загруженных файлов, размеры загруженных файлов и типы загруженных файлов. Порядок элемен-тов в массивах в точности соответствует порядку полей в исходной форме — так, если имя файла file.zip было введено в первое поле типа file, то относящиеся к этому файлу переменные будут располагаться в элементах перечисленных массивов с индексом 0 (не забывайте — ну-мерация элементов массивов начинается с нуля!).

    Данные массивы будут переданы в обработчик во всех версиях PHP, начиная с 3.0.1, если в файле php.ini включена опция regis-ter_globals.

    2. Массивы $HTTP_POST_FILES[‘uploadfile’][‘tmp_name’][], $HTTP_POST_FILES[‘uploadfile’][‘name’][], $HTTP_POST_FILES [‘up-loadfile’][‘size’][] и $HTTP_POST_FILES[‘uploadfile’][‘type’][], содер-жащие соответственно временные имена загруженных файлов, ис-ходные имена загруженных файлов, размеры загруженных файлов и типы загруженных файлов. Порядок элементов в массивах опять-таки в точности соответствует порядку полей в исходной форме.

    Данные массивы будут переданы в обработчик во всех версиях PHP, начиная с 3.0.1, если в файле php.ini3 включена опция track_vars.

    3. Массивы $_FILES[‘uploadfile’][‘name’][], $_FILES[‘uploadfile’] [‘tmp_name’][], $_FILES[‘uploadfile’][‘size’][] и $_FILES[‘uploadfile’] [‘type’][]. Их содержимое аналогично предыдущим. Данные массивы будут переданы в обработчик в версиях PHP, начиная с 4.1.

    В данных примерах uploadfile — это всего лишь имя массива, вы, естественно, можете назвать его по-другому.

    В PHP 3-й версии с номером подверсии 3.0.17 и выше, а также на-чиная с версии 4.0.3, в PHP специально для работы с загруженными через форму файлами есть две команды — is_uploaded_file и move_uploaded_file. При использовании вышеописанного спо-соба загрузки файлов есть риск того, что некий злоумышленник вме-сто указания в форме реального файла со своего компьютера укажет путь к какому-либо файлу на web-сервере (например, файлу с паро-лями пользователей), и тем самым сценарий, обрабатывающий за-груженный файл, будет работать уже с этим файлом, — что, ясное де-ло, весьма нежелательно. С помощью же данных команд можно ис-ключить такую возможность. Первая команда проверяет, был ли тот файл, который указан в ее параметре, загружен через форму загрузки файлов из браузера посетителя, и если да, то возвращает True, в про-тивном случае — False. Вторая же аналогична команде copy, однако в качестве исходного файла для копирования в ней допустимо указы-вать лишь загруженный от посетителя файл — в противном случае она не сработает, вернув False (без вывода в документ каких-либо преду-преждений — в отличие от ситуации неудачи копирования по каким-либо другим причинам).

    Команда is_uploaded_file() по сути дела проверяет, находится ли ука-занный в ее параметрах файл во временной директории сервера — то есть той, куда все загружаемые файлы первоначально помещаются. То же самое проверяет и move_uploaded_file() перед началом копирования.

    Так что, как видите, предоставить посетителям возможность за-гружать файлы на ваш сайт довольно просто. Как и создать простей-шую, но довольно эффективную систему ограничения этой возмож-ности по паролю — всего лишь с помощью проверки передаваемой через форму переменной, содержащей пароль; сам же пароль указы-вается в тексте программы на PHP. Для того, чтобы дать вашему дру-гу возможность вести свою «колонку» на вашем сайте, этого будет достаточно.

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

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

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