Как загрузить файл на сервер через форму
Форма для загрузки обязательно отправляется методом POST и с атрибутом enctype="multipart/form-data" — без него браузер пришлёт только имя файла, а не сам файл. Файл upload.php с формой и обработкой:
<?php
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$file = $_FILES['photo'] ?? null;
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];
$maxSize = 2 * 1024 * 1024;
if (!isset($file['error']) || $file['error'] !== UPLOAD_ERR_OK) {
$message = 'Файл не загружен';
} elseif ($file['size'] > $maxSize) {
$message = 'Файл больше 2 МБ';
} else {
$type = (new finfo(FILEINFO_MIME_TYPE))->file($file['tmp_name']);
if (!isset($allowed[$type])) {
$message = 'Можно загружать только JPEG, PNG и WebP';
} else {
$name = bin2hex(random_bytes(16)) . '.' . $allowed[$type];
if (move_uploaded_file($file['tmp_name'], __DIR__ . '/uploads/' . $name)) {
$message = 'Файл сохранён как ' . $name;
} else {
$message = 'Не удалось сохранить файл';
}
}
}
}
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Загрузка фото</title>
</head>
<body>
<?php if ($message !== ''): ?>
<p><?= htmlspecialchars($message) ?></p>
<?php endif; ?>
<form method="post" enctype="multipart/form-data">
<input type="file" name="photo" accept="image/jpeg,image/png,image/webp">
<button>Загрузить</button>
</form>
</body>
</html>Рядом с upload.php создайте папку uploads, и у веб-сервера должно быть право писать в неё (урок Файлы и подключение).
Что проверяется и почему
Загруженный файл PHP кладёт во временную папку, а сведения о нём — в $_FILES['photo']: name (имя на компьютере посетителя), type (тип, который назвал браузер), size (размер в байтах), tmp_name (путь к временному файлу), error (код ошибки). Временный файл удаляется в конце запроса, поэтому его нужно перенести функцией move_uploaded_file.
- Код ошибки.
UPLOAD_ERR_OK(0) — файл пришёл целиком. Остальные коды:UPLOAD_ERR_INI_SIZE— больше лимитаphp.ini,UPLOAD_ERR_PARTIAL— загрузился частично,UPLOAD_ERR_NO_FILE— файл не выбран. - Размер — свой предел, даже если сервер разрешает больше.
- Настоящий тип. Расширение в имени и поле
typeзадаёт посетитель — им верить нельзя.finfoсмотрит на содержимое файла. Нужно расширениеfileinfo: в Ubuntu оно есть сразу, в Windows включается строкойextension=fileinfoвphp.ini. - Новое имя. Имя посетителя не используется вовсе: в нём могут быть
../, русские буквы, пробелы или расширение.php.random_bytesдаёт случайное имя, а расширение берётся из списка разрешённых типов.
Главная опасность загрузки — файл с кодом. Если на сервер попадёт shell.php и его можно открыть по адресу, злоумышленник выполнит на сервере любые команды. Поэтому: только типы из короткого списка, имя и расширение — свои, папку загрузок лучше держать вне корня сайта (папки, которую веб-сервер отдаёт по адресам) или запретить в ней выполнение PHP настройкой веб-сервера.
Лимиты в php.ini
upload_max_filesize = 2M
post_max_size = 8M
max_file_uploads = 20Это значения по умолчанию. upload_max_filesize — предел одного файла, post_max_size — всего запроса вместе с полями, он должен быть больше. Если запрос превысил post_max_size, PHP не заполнит ни $_POST, ни $_FILES: массивы окажутся пустыми, как будто форму не отправляли. После правки php.ini перезапустите веб-сервер или PHP-FPM.
Если не работает
$_FILESпустой — у формы нетenctype="multipart/form-data"или методаpost, либо превышенpost_max_size.- Ошибка с кодом 1 (
UPLOAD_ERR_INI_SIZE) — файл большеupload_max_filesize. move_uploaded_fileвернулfalseс предупреждениемPermission denied— нет права записи в папкуuploads, или папки нет.- Несколько файлов: поле
name="photos[]"с атрибутомmultiple— тогда в$_FILES['photos']['name'],['tmp_name']и остальных будут массивы, и проверять нужно каждый файл.
Загруженные картинки выводят по сохранённому имени: <img src="/uploads/<?= htmlspecialchars($name) ?>">, если папка доступна из браузера.