Первый сайт на PHP

         

PHP: ЗАКАЧКА ФАЙЛОВ

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

ПОДГОТОВКА

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

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

Рис.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.ini - параметр regis-ter_globals установлен в on) содержащих информацию о загруженном файле. Именно на их основе сценарий на ней сможет работать с загруженным файлом. Кроме того, эти же самые переменные помещаются в массив $HTTP_POST_FILES ( Если в конфигурационном файле РНР -php.ini - параметр track_varsустановлен в on), а в РНР версий 4.1 и выше -и в массив SFILES (в отличие от переменных и массива SHTTPPOSTFILES этот массив по умолчанию доступен и во всех функциях, размещенных на странице с программой-обработчиком загруженных файлов).
Вот эти переменные:

1. Переменная, имеющая то же имя, что и поле с типом file в исходной форме. Если оно выглядело как < input name= "uploadf ile" type= " f ile">, то переменная будет иметь имя $uploadfile (и соответственно еще $HTTP_POST_FILES['uploadfile']['tmp_name'], $_FILES [ ' uploadf ile ' ] [ ' tmp_name ' ]). В эту переменную записывается то имя (временное, создающееся автоматически), которое загруженный файл имеет в папке временных файлов. Именно с ним будут работать команды копирования файла.
Если в исходной форме присутствовало несколько полей типа file с разными именами, то для каждого из них создается своя переменная со значением, относящимся к соответствующему файлу. 2. Переменная, имеющая имя "Переменная 1 jiame" - т. е. к имени первой переменной просто приписывается горизонтальная черточка и слово name, например, для вышеуказанного примера ее имя будет выглядеть как $uploadf ile_name (ну и, разумеется, то же самое значение получат элементы вышеупомянутых массивов $HTTP_POST_FILES['uploadfile']['name'], $_FILES ['uploadf ile ' ] ['name ' ]). Ее значением является исходное имя файла в системе отправителя. 3. Переменная, имеющая имя "Переменная 1_size". Ее значение -размер загруженного файла в байтах. 4. Переменная, имеющая имя "Переменная 1_type". Ее значение -тип загруженного файла согласно спецификации MIME (например, "image/gif").

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

ФОРМА ДЛЯ ПОЛЬЗОВАТЕЛЯ

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

<FORM ENCTYPE="multipart/form-data" ACTION "up . php" METHOD=POST>

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

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

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

Ваш пароль: <INPUT NAME="pass" TYPE="password">

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

Закачать файл:<INPUT NAME="zak" TYPE="file">

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

<INPUT TYPE="submit" VALUE="Закачать">

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

</FORM>

СЦЕНАРИЙ ОБРАБОТКИ

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

<?php

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

if ($zak=="none")
{echo ("Вы забыли указать файл...");}

Рис.7.4. Сообщение пользователю в случае неуказания имени файла

Примечание:
Команда elseif используется в операторе if для того, чтобы проводить проверку какого-либо условия в том случае, когда то условие, которое указано в заглавии оператора if, неверно. Она идентична конструкции:
}
else
{
if (выражение) {
} }
и введена в РНР для упрощения синтаксиса программ.

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

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

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

elseif ($pass!="parol")
{ echo ("Ваш пароль неверен!");}

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

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

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

elseif (copy($zak, "user/$zak_name"))
{echo("Файл $zak_name размером $zak_size байт успешно загружен на сайт.");}

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

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

else
{ echo("He удалось скопировать $zak_name");}
Конец скрипта:
?>

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

Вот, собственно, и все. Пользователь, зайдя на первую страницу (рис.7.3), должен ввести свой пароль и найти с помощью открывающегося при нажатии на кнопку "Обзор" диалогового окна файл для загрузки. После нажатия кнопки "Загрузить" он увидит одно из трех сообщений - см. рис. 7.4, 7.5, 7.6.
Если вместо имен, содержащих информацию о загружаемом файле и передаваемых через форму переменных, использовать имена соответствующих элементов массивов $HTTP_POST_FILES и $HTTP_POST_VARS, то код обработчика будет выглядеть так:

<?php
if ($HTTP_POST_FILES['zak']['name']=="none") {echo ("Вы забыли указать файл...");}
elseif ($HTTP_POST_VARS['pass']!="parol") { echo ("Ваш пароль неверен!");}
elseif
(copy($HTTP_POST_FILES['zak']['tmp_name'], "user/".$HTTP_POST_FILES['zak']['name']))
{echo ("Файл ".$HTTP_POST_FILES['zak1] ['name'] ." размером ".$HTTP_POST_FILES['zak']['size']." байт успешно загружен на сайт.");}
else
echo("Hfe удалось скопировать ".
$HTTP_POST_FILES['zak']['name']);} ?>

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

echo ("Имя файла: $HTTP_POST_FILES['zak']['name']")

Правильно:

echo ("Имя файла:". $HTTP_POST_FILES t'zak'] ['name'])

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

<input name="uploadfile[]" type="file">
<input name="uploadfile[]" type="file">
<input name="uploadfile[]" type="file">

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

1. Массивы $uploadfile[], $uploadfile_name[], $uploadfile_size[], $uploadfile_type[], содержащие соответственно временные имена загруженных файлов, исходные имена загруженных файлов, размеры загруженных файлов и типы загруженных файлов. Порядок элементов в массивах в точности соответствует порядку полей в исходной форме - так, если имя файла file.zip было введено в первое поле типа file, то относящиеся к этому файлу переменные будут располагаться в элементах перечисленных массивов с индексом 0 (не забывайте - нумерация элементов массивов начинается с нуля!).
Данные массивы будут переданы в обработчик во всех версиях РНР, начиная с 3.0.1, если в файле php.ini ( Настройка этого файла - привилегия администратора web-cepвepa, так что если вы таковым не являетесь, то включить вы ее не сумеете) включена опция regis-ter_globals. 2. Массивы $HTTP_POST_FILES['uploadfile']['tmp_name'][], $HTTP_POST_FILES[luploadfile')['name'][], $HTTP_POST_FILES ['up-loadfile'H'size'][] и $HTTP_POST_FILES['uploadfile']['type'][], содержащие соответственно временные имена загруженных файлов, исходные имена загруженных файлов, размеры загруженных файлов и типы загруженных файлов. Порядок элементов в массивах опять-таки в точности соответствует порядку полей в исходной форме.
Данные массивы будут переданы в обработчик во всех версиях РНР, начиная с 3.0.1, если в файле php.ini включена опция track_vars. 3. Массивы $_FILES['uploadfile']['name'][], $_FILES['uploadfile'] ['tmpjiame'JO, $_FILES['uploadfile']['size'][] u $_FlLES['uploadfile'] I'type'Jl]. Их содержимое аналогично предыдущим. Данные массивы будут переданы в обработчик в версиях РНР, начиная с 4.1.

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

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

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