Управление размером загружаемых файлов в среде PHP
Настройка загрузки файлов в PHP: параметры upload_max_filesize и post_max_size
Параметры upload_max_filesize и post_max_size в файле php.ini определяют максимальные размеры загружаемых файлов и всего POST-запроса соответственно. Корректная настройка необходима для работы форм с файлами, изображениями, архивами.
Основное решение: изменение файла php.ini
Как увеличить лимит загрузки файлов через прямой доступ к php.ini?
Наиболее надёжный способ - редактирование основного конфигурационного файла PHP. Необходимо найти файл php.ini (обычно в /etc/php/версия/cli/ или /etc/php/версия/apache2/), изменить значения и перезапустить веб-сервер.
; Пример: установка максимального размера одного файла 20MB
upload_max_filesize = 20M
; Максимальный размер всего POST-запроса (должен быть больше upload_max_filesize)
post_max_size = 25M
; Также стоит проверить memory_limit (рекомендуется > post_max_size)
memory_limit = 128M
; Максимальное время выполнения для загрузки (в секундах)
max_execution_time = 300
max_input_time = 300
После изменений выполнить команду:
sudo systemctl restart apache2 # для Apache
sudo systemctl restart nginx # для Nginx с php-fpm
Типичные проблемы:
- Значение post_max_size меньше upload_max_filesize - загрузка файла объёмом больше post_max_size приведёт к ошибке.
- Недостаточный memory_limit - при обработке больших файлов скрипт может превысить лимит памяти.
- Игнорирование max_execution_time и max_input_time - загрузка большого файла может прерваться по таймауту.
Альтернативные способы настройки
Как изменить лимиты через файл .htaccess на Apache?
Если нет доступа к php.ini, но используется Apache с mod_php или mod_suexec, можно применить директиву php_value в .htaccess.
php_value upload_max_filesize 20M
php_value post_max_size 25M
php_value max_execution_time 300
php_value max_input_time 300
Файл .htaccess должен находиться в корневой директории сайта. Действие распространяется на все вложенные каталоги.
Возможные проблемы:
- Директивы могут быть заблокированы настройками сервера (AllowOverride не включает опции PHP).
- Использование php_value в .htaccess невозможно при работе через FastCGI (например, php-fpm).
Решение:
Проверить конфигурацию Apache: AllowOverride All или использовать другой способ.
Как настроить параметры через файл .user.ini для PHP-FPM (CGI/FastCGI)?
При использовании PHP-FPM или CGI разрешён файл .user.ini (аналог .htaccess для PHP). Создать в корневой директории сайта файл .user.ini с теми же директивами, что в php.ini.
upload_max_filesize = 30M
post_max_size = 35M
max_execution_time = 600
Изменения вступают в силу без перезагрузки сервера, но кешируются на несколько секунд.
Ошибки:
- Файл .user.ini не срабатывает, если не настроен сервер на его чтение (по умолчанию включено).
- Директива memory_limit в .user.ini может быть недоступна (зависит от режима работы).
Как изменить лимиты непосредственно в PHP-скрипте с помощью ini_set()?
Функция ini_set() позволяет менять некоторые параметры во время выполнения скрипта. Подходит для гибкого управления (например, разные лимиты для разных форм).
<?php
ini_set('upload_max_filesize', '50M');
ini_set('post_max_size', '60M');
ini_set('max_execution_time', '600');
ini_set('max_input_time', '600');
// Обратите внимание: изменения действуют только в текущем скрипте
?>
Важно разместить вызовы ini_set в самом начале скрипта, до любого вывода. Параметр memory_limit с помощью ini_set изменить нельзя, если режим PHP запрещает (обычно разрешено).
Ограничения:
- Некоторые хосты блокируют изменение критичных параметров через ini_set.
- Параметры восстанавливаются после завершения скрипта; не влияют на другие запросы.
- Не работает, если в PHP-конфигурации установлен режим PHP_INI_SYSTEM для директивы (upload_max_filesize имеет режим PHP_INI_PERDIR, поэтому доступен).
Как настроить лимиты загрузки в Nginx с PHP-FPM?
Nginx имеет собственный лимит размера тела запроса client_max_body_size. Он должен быть не меньше post_max_size.
# В блоке server или location:
client_max_body_size 50M;
# Также можно ограничить время:
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
fastcgi_send_timeout 600;
fastcgi_read_timeout 600;
После изменений перезагрузить Nginx.
sudo systemctl reload nginx
Частая ошибка:
Загрузка файла, превышающего client_max_body_size, приводит к ошибке 413 (Request Entity Too Large). Необходимо синхронизировать настройки Nginx и PHP.
Общие рекомендации и проверка
После настройки полезно создать тестовый скрипт для проверки:
<?php
phpinfo();
// Найдите строки upload_max_filesize, post_max_size, memory_limit
?>
Или создать форму загрузки и протестировать с файлом нужного размера.
Расширенные примеры настройки и работы с загрузкой файлов
Пример 1: Скрипт загрузки с проверкой лимитов и обработкой ошибок
<?php
// Получаем текущие лимиты
$uploadMax = ini_get('upload_max_filesize');
$postMax = ini_get('post_max_size');
$memoryLimit = ini_get('memory_limit');
echo "Текущие лимиты: upload_max_filesize = $uploadMax, post_max_size = $postMax, memory_limit = $memoryLimit\n";
// Устанавливаем свои (если разрешено)
ini_set('upload_max_filesize', '100M');
ini_set('post_max_size', '110M');
ini_set('memory_limit', '256M'); // может не сработать
ini_set('max_execution_time', '600');
// Проверяем доступность загрузки
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
$file = $_FILES['file'];
if ($file['error'] !== UPLOAD_ERR_OK) {
// Обработка ошибок
$errorMessages = [
UPLOAD_ERR_INI_SIZE => 'Размер файла превышает upload_max_filesize в php.ini',
UPLOAD_ERR_FORM_SIZE => 'Размер файла превышает MAX_FILE_SIZE в HTML форме',
UPLOAD_ERR_PARTIAL => 'Файл был загружен только частично',
UPLOAD_ERR_NO_FILE => 'Файл не был загружен',
UPLOAD_ERR_NO_TMP_DIR => 'Отсутствует временная папка',
UPLOAD_ERR_CANT_WRITE => 'Не удалось записать файл на диск',
UPLOAD_ERR_EXTENSION => 'PHP расширение остановило загрузку',
];
echo "Ошибка: " . ($errorMessages[$file['error']] ?? 'Неизвестная ошибка');
} else {
echo "Файл '{$file['name']}' успешно загружен (размер: {$file['size']} байт).";
}
}
?>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="20971520" /><!-- 20MB -->
<input type="file" name="file" />
<input type="submit" value="Загрузить" />
</form>
Вывод при попытке загрузить файл > upload_max_filesize: Текущие лимиты: upload_max_filesize = 20M, post_max_size = 25M, memory_limit = 128M Ошибка: Размер файла превышает upload_max_filesize в php.ini
Пример 2: Загрузка нескольких файлов и проверка суммарного размера
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['files'])) {
$totalSize = 0;
$errors = [];
foreach ($_FILES['files']['tmp_name'] as $key => $tmpName) {
if ($_FILES['files']['error'][$key] === UPLOAD_ERR_OK) {
$totalSize += $_FILES['files']['size'][$key];
} else {
$errors[] = "Файл '{$_FILES['files']['name'][$key]}' не загружен (код ошибки {$_FILES['files']['error'][$key]})";
}
}
$postMaxBytes = (int)ini_get('post_max_size');
// Преобразование шорт-значений в байты
$unit = strtoupper(substr(ini_get('post_max_size'), -1));
$value = (int)ini_get('post_max_size');
if ($unit === 'M') $postMaxBytes = $value * 1024 * 1024;
elseif ($unit === 'K') $postMaxBytes = $value * 1024;
// Аналогично для G
echo "Суммарный размер загруженных файлов: $totalSize байт. Лимит POST: $postMaxBytes байт.\n";
if (!empty($errors)) {
echo "Ошибки:\n" . implode("\n", $errors);
}
}
?>
<form method="post" enctype="multipart/form-data" accept-charset="utf-8">
<input type="file" name="files[]" multiple />
<input type="submit" value="Загрузить несколько файлов" />
</form>
Пример вывода при успешной загрузке двух файлов по 5MB: Суммарный размер загруженных файлов: 10485760 байт. Лимит POST: 26214400 байт. Ошибок нет.
Пример 3: Динамическое определение лимита и предупреждение пользователя
<?php
function getMaxFileSize() {
// Возвращает максимальный размер файла в байтах, учитывая upload_max_filesize и post_max_size
$uploadMax = parseSize(ini_get('upload_max_filesize'));
$postMax = parseSize(ini_get('post_max_size'));
// Для одного файла лимит равен upload_max_filesize, но с учётом остальных данных POST
return $uploadMax; // обычно меньше или равен post_max_size
}
function parseSize($size) {
$unit = preg_replace('/[^bBkKmMgG]/', '', $size);
$value = (int) $size;
switch (strtoupper($unit)) {
case 'G': return $value * 1024 * 1024 * 1024;
case 'M': return $value * 1024 * 1024;
case 'K': return $value * 1024;
default: return $value;
}
}
$maxFileSize = getMaxFileSize();
echo "Максимальный размер одного файла: " . ($maxFileSize / 1024 / 1024) . " MB";
?>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $maxFileSize; ?>" />
<input type="file" name="file" />
<input type="submit" value="Загрузить" />
</form>
Результат: на странице отображается "Максимальный размер одного файла: 20 MB", форма скрыто передаёт лимит.
Пример 4: Настройка .user.ini для разных подпапок
# В корне сайта /var/www/html/ создан .user.ini с:
upload_max_filesize = 10M
# В подпапке /var/www/html/upload/ создан другой .user.ini с:
upload_max_filesize = 100M
# Теперь файлы, загружаемые через скрипты в /upload/, могут иметь размер до 100M.
# При этом в корне сохраняется лимит 10M.
При обращении к скрипту в /upload/ значение upload_max_filesize будет 100M, что можно проверить через phpinfo().
Пример 5: Логгирование ошибок загрузки при превышении лимитов
<?php
// Включаем логгирование ошибок PHP
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php_upload_errors.log');
error_reporting(E_ALL);
// После попытки загрузки
if ($_FILES['file']['error'] === UPLOAD_ERR_INI_SIZE) {
error_log("Превышен upload_max_filesize: файл {$_FILES['file']['name']}, размер {$_FILES['file']['size']}");
}
// В лог будет записано сообщение.
?>
Содержимое /var/log/php_upload_errors.log: [15-Mar-2025 12:34:56 Europe/Moscow] PHP Notice: Превышен upload_max_filesize: файл large.zip, размер 209715200