A free and open-source book on ZF3 for beginners


10.1. О выгрузке файлов на сервер по протоколу HTTP

HTML-формы могут выгружать (upload) файлы сколь угодно больших размеров 39. Файлы, как правило, передаются через HTTP-метод POST 40.

39) Выгрузка файлов по HTTP описана в RFC-1867. Этот механизм позволяет выгружать большие файлы, используя бинарное кодирование передаваемых данных. Для этой цели используется тип кодировки "multipart/form-data".

40) HTTP-метод GET неэффективен для выгрузки файлов, так как у длины URL есть предел. Кроме того, URL-кодирование данных файлов значительно увеличивает длину URL.

По умолчанию, HTTP использует URL-кодирование для передачи данных форм. Вы могли видеть это кодирование в предыдущих главах. Однако, оно неэффективно для выгрузки больших файлов, так как такое кодирование бинарных данных сильно увеличивает длину HTTP-запроса. Для выгрузки данных лучше использовать так называемое бинарное транспортное кодирование (binary transfer encoding), рассматриваемое в следующем разделе.

10.1.1. Бинарное транспортное кодирование HTTP

Простая HTML-форма с возможностью выгрузки файлов показана в примере кода ниже. Бинарный тип кодирования устанавливается заданием атрибута enctype формы со значением "multipart/form-data":

<form action="upload" method="POST" enctype="multipart/form-data">
    <input type="file" name="myfile">
    <br/>
    <input type="submit" name="Submit">
</form>

В строке 1 мы явно задаем кодирование "multipart/form-data" (атрибут `enctype) для использования эффективного бинарного кодирования передачи данных формы.

В строке 2 мы определяем поле ввода с типом "file" и именем "myfile". Это поле позволит посетителям сайта выбирать файл для выгрузки.

Если вы сохраните представленную выше разметку в файл .html и откроете его в своем браузере, вы увидите страницу как на рисунке 10.1.

Рисунок 10.1. Простая HTML-форма с возможностью выгрузки файлов Рисунок 10.1. Простая HTML-форма с возможностью выгрузки файлов

Элемент файла имеет кнопку поиска Browse... , которая позволяет выбрать файл для выгрузки. После того как пользователь сайта выбрал файл и нажал кнопку отправки формы, веб-браузер посылает веб-серверу HTTP-запрос, содержащий данные выгружаемого файла. Пример ниже показывает, как может выглядеть HTTP-запрос:

POST http://localhost/upload HTTP/1.1
Host: localhost
Content-Length: 488
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) 
Content-Type: multipart/form-data; boundary=----j1bOrwgLvOC3dy7o
Accept-Encoding: gzip,deflate,sdch

------j1bOrwgLvOC3dy7o
Content-Disposition: form-data; name="myfile"; filename="Somefile.txt"
Content-Type: text/html

(двоичные данные файла)
------j1bOrwgLvOC3dy7o
Content-Disposition: form-data; name="Submit"

Отправка запроса
------j1bOrwgLvOC3dy7o--

Как видите, HTTP-запрос с типом кодирования "multipart/form-data" выглядит аналогично обычному HTTP-запросу (содержит строку статуса, заголовки и область содержимого), однако он имеет следующие важные отличия:

По умолчанию, настройки PHP-движка не позволяют выгружать большие файлы (больше 2МБ). Для выгрузки таких файлов нужно изменить файл конфигурации php.ini, а также параметры post_max_size и upload_max_filesize. О том, как это сделать, можете прочитать в Приложение А. Организация среды веб-разработки.. Установив эти настройки на 100M позволит выгружать на сервер файлы размером до 100 Мб, и этого, как правило, будет достаточно. Если вы планируете выгрузку очень больших файлов вплоть до 1 ГБ, лучше задайте в настройках 1024М. Не забудьте перезапустить веб-сервер Apache после изменения файла конфигурации.

10.1.2. Суперглобальный массив $_FILES в PHP

При выгрузке посетителем сайта файлов на ваш веб-сервер Apache, эти файлы помещаются во временное хранилище (обычно в системный временный каталог - /tmp в Linux и C:\Windows\Temp в Windows). PHP-скрипт принимает информацию о файле в специальный суперглобальный массив $_FILES.

Массив $_FILES аналогичен суперглобальным $_GET и $_POST. Два последних используются для хранения переменных GET и POST соответственно, в то время как первый используется для хранения информации о выгружаемых на сервер файлах.

Например, для вышеупомянутой формы выгрузки суперглобальный массив $_FILES будет выглядеть следующим образом (выходные данные генерируются с помощью PHP-функции var_dump()):

array (size=1)
    'myfile' => 
        array (size=5)
            'name' => string 'somefile.txt' (length=12)
            'type' => string 'text/plain' (length=10)
            'tmp_name' => string '/tmp/phpDC66.tmp' (length=16)
            'error' => int 0
            'size' => int 18

Как видите из этого примера, массив $_FILES содержит запись для каждого выгружаемого на сервер файла. Для каждого файла он содержит следующую информацию:

41) MIME-тип (Multipurpose Internet Mail Extension - Многоцелевое расширение почты Интернета), также известный как "тип содержимого" - стандартный идентификатор, используемый в Интернете для указания типа данных, которые содержит файл. Например, MIME-тип "text/plain" присваивается текстовому файлу, а MIME-тип "application/octet-stream" - бинарному.

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

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

Следующий фрагмент кода показывает, как перемещать файл, выгруженный на сервер с помощью простой формы, которую мы рассмотрели выше:

$destPath = '/path/to/your/upload/dir';
$result = move_uploaded_file($_FILES['myfile']['tmp_name'], $destPath);
if(!$result) {
    // Возникла какая-то ошибка.
}

В строке 1 этого примера мы задаем $destPath - имя каталога, куда нужно сохранить выгруженный файл.

В строке 2 мы вызываем функцию move_uploaded_file() и передаем ей два аргумента: путь к временному файлу и путь назначения.

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

В строке 3 мы проверяем возвращаемое значение функции. При успешной операции функция вернет true. Если произошли какие-либо ошибки (например, если прав доступа к каталогу недостаточно для сохранения файла), вернется булевое false.


Top