Загрузка файлов

Файл может быть загружен на сервер через multi-part HTTP POST запрос. Например, форма для загрузки файла на сервер:

<form enctype="multipart/form-data" action="index.php"
      method="post">
    <input type="hidden" name="MAX_FILE_SIZE"
           value="50000" />
    <input name="filedata" type="file" />
    <input type="submit" value="Send file" />
</form>

Чтоб ограничить количество данных, загружаемых через POST, можно использовать следующие директивы:

  • post_max_size
  • max_input_time
  • upload_max_filesize

Как только файл загружен на сервер, PHP хранит его во временной директории и делает его доступным для скрипта, который был вызван POST запросом. Задачей скрипта уже является переместить файл в нужное место, так как после выполнения скрипта файл будет уничтожен.

Внутри скрипта загруженные файлы становятся доступными в $_FILES суперглобальном массиве. Каждый ключ в этом массиве - это имя элемента из HTML формы. Сам по себе этот элемент является массивом со следующими элементами:

Ключ Описание
name Оригинальное имя файла.
type MIME-тип файла, переданный браузером.
size Размер в байтах.
tmp_name Имя файла во временной директории.
error Код ошибки для этого файла. Значение UPLOAD_ERR_OK означает, что успешную загрузку, в то время как любой другой код означает, что произошла ошибка.

Реальная проблема состоит в том, что почти вся информация из $FILES может быть подделана передачей вредоносной информацией через HTTP заголовок. Есть несколько возможностей в PHP проверить успешную загрузку:

  • Проверка ключа error на соответствие с UPLOAD_ERR_OK.
  • Размер size не нулевой.
  • Имя tmp_name задано.

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

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

Замечание: при загрузке файла на сервер методом PUT файл будет доступен в потоке php://input:

<?php
/* PUT данные приходят в потоке ввода stdin */
$putdata = fopen("php://input", "r");

/* Открываем файл на чтение */
$fp = fopen("myputfile.ext", "w");

/* Читаем 1 KB данных за один раз
   и пишем в файл */
while ($data = fread($putdata, 1024))
  fwrite($fp, $data);

/* Закрываем потоки */
fclose($fp);
fclose($putdata);
?>

Отслеживание прогресса загрузки файлов с помощью сессий

PHP может отслеживать прогресс загрузки отдельных файлов при включенной опции session.upload_progress.enabled. Данная информация не особенно полезна для запроса, непосредственно закачивающего файл, однако, в течение данной загрузки приложение может посылать POST-запросы на отдельную страницу (например, с помощью XHR) для проверки статуса.

Прогресс закачки будет доступен в суперглобальной переменной $_SESSION в процессе закачки, а также при отправке POST-запросом переменной с именем равным значению опции session.upload_progress.name. Как только PHP обнаружит такой POST-запрос, он создаст массив в $_SESSION, ключом которого будет конкатенация значений опций session.upload_progress.prefix и session.upload_progress.name. Ключ обычно можно получить прочитав эти опции, т.е.:

<?php
$key = ini_get("session.upload_progress.prefix") . $_POST[ini_get("session.upload_progress.name")];
var_dump($_SESSION[$key]);
?>

Также возможно отменить загружаемый в данный момент файл, установив ключ $_SESSION[$key]["cancel_upload"] в значение TRUE. При загрузке нескольких файлов за один раз, это действие отменит только текущий загружаемый файл и все следующие за ним, но не удалит уже успешно загруженные к этому времени файлы. Если закачка была отменена этим способом, то элемент с ключом error в массиве $_FILES будет установлен в UPLOAD_ERR_EXTENSION.

Опции session.upload_progress.freq и session.upload_progress.min_freq контролируют частоту обновления информации о прогрессе загрузки. При разумных значениях этих двух настроек, накладные расходы данной функции практически неощутимы.