Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

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

Аватар, фотография товара, документ — пользователи постоянно загружают файлы. PHP принимает файл сам и кладёт его во временную папку; ваша задача — проверить его и перенести в постоянное место.

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

Форма

Чтобы форма могла отправлять файлы, ей нужен метод post и атрибут enctype:

<form method="post" action="/upload.php" enctype="multipart/form-data">
  <input type="file" name="avatar">
  <button type="submit">Загрузить</button>
</form>

Без enctype="multipart/form-data" на сервер придёт только имя файла.

Массив $_FILES

Сведения о загруженном файле лежат в $_FILES под именем поля:

<?php
 
$file = $_FILES['avatar'];
 
echo $file['name'];     // photo.jpg — имя на компьютере пользователя
echo $file['size'];     // размер в байтах
echo $file['tmp_name']; // путь к временному файлу на сервере
echo $file['error'];    // код ошибки, 0 — всё в порядке

Проверка ошибки

Первым делом проверяют, что файл действительно загрузился:

<?php
 
$file = $_FILES['avatar'] ?? null;
 
if ($file === null || $file['error'] !== UPLOAD_ERR_OK) {
    exit('Файл не загружен');
}

Ошибка возникает, если файл не выбран или превышает лимит из настроек сервера upload_max_filesize.

Проверка размера и типа

Имени файла и типу, которые прислал браузер, доверять нельзя — их легко подделать. Настоящий тип определяют по содержимому:

<?php
 
$file = $_FILES['avatar'];
 
if ($file['size'] > 2 * 1024 * 1024) {
    exit('Файл больше 2 МБ');
}
 
$type = mime_content_type($file['tmp_name']);
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];
 
if (!isset($allowed[$type])) {
    exit('Разрешены только картинки');
}

Сохранение

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

<?php
 
$file = $_FILES['avatar'];
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];
$extension = $allowed[mime_content_type($file['tmp_name'])];
 
$name = bin2hex(random_bytes(16)) . '.' . $extension;
$target = __DIR__ . '/uploads/' . $name;
 
if (move_uploaded_file($file['tmp_name'], $target)) {
    echo 'Сохранено: ' . $name;
}

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

Несколько файлов

Полю дают имя с квадратными скобками и атрибут multiple. Тогда каждый элемент $_FILES становится массивом:

<?php
 
foreach ($_FILES['photos']['tmp_name'] as $index => $tmpName) {
    if ($_FILES['photos']['error'][$index] === UPLOAD_ERR_OK) {
        echo $_FILES['photos']['name'][$index];
    }
}

Частая ошибка

Нельзя сохранять файл под именем, которое прислал пользователь. Файл shell.php в папке, доступной из браузера, превратится в чужой код на вашем сервере, а имя вида ../../index.php перезапишет файлы сайта. Проверяйте содержимое, давайте своё имя и храните загрузки в папке, где сервер не выполняет PHP.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.