Реализация upload файлов на сервер в PHP

Раздел: Разработка на PHP -> Работа с файлами

Основные методы загрузки файлов

Как загрузить один файл на сервер с помощью PHP?

Наиболее эффективное решение для загрузки одного файла заключается в использовании функции move_uploaded_file вместе с суперглобальным массивом $_FILES. Этот подход обеспечивает безопасное перемещение временного файла в постоянное место хранения. Пример простейшей HTML-формы:

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

Index php path (пути файлов в php)

PHP-обработчик upload.php:

<?php
$uploadDir = 'uploads/';
$file = $_FILES['file'];
$filename = basename($file['name']);
$targetFile = $uploadDir . $filename;

if (move_uploaded_file($file['tmp_name'], $targetFile)) {
    echo 'Файл успешно загружен.';
} else {
    echo 'Ошибка загрузки файла.';
}
?>

Php tmp files (временные файлы php)

После отправки формы PHP автоматически сохраняет загруженный файл во временную директорию. Ключ $file['tmp_name'] содержит путь к этому временному файлу. Функция move_uploaded_file проверяет, что файл действительно был загружен через HTTP POST, и перемещает его в целевую папку. Папка uploads должна существовать и иметь права на запись.

Типичные ошибки и их решения

  • Файл не загружается (UPLOAD_ERR_NO_FILE): форма не содержит файла. Проверьте атрибут enctype и наличие имени поля в форме.
  • Ошибка перемещения файла (UPLOAD_ERR_CANT_WRITE): нет прав на запись в целевую папку. Установите корректные разрешения, например 755 или 777 для папки uploads.
  • Превышение размера файла (UPLOAD_ERR_INI_SIZE или UPLOAD_ERR_FORM_SIZE): файл превышает лимиты, заданные в php.ini (upload_max_filesize, post_max_size) или в скрытом поле MAX_FILE_SIZE формы.
  • Угроза безопасности: злоумышленник может отправить имя с символами ../ для обхода директорий. Использование basename() не гарантирует полной защиты. Рекомендуется генерировать уникальное имя с помощью uniqid() или сохранять файл с проверкой расширения.

Как загрузить несколько файлов одновременно?

Для загрузки нескольких файлов используйте HTML-атрибут multiple и имя поля в виде массива: name="files[]". PHP автоматически создаст массив в $_FILES['files'] с теми же ключами (name, tmp_name и др.), каждый из которых также будет массивом значений. Пример формы:

<form action="upload_multi.php" method="post" enctype="multipart/form-data">
    <input type="file" name="files[]" multiple>
    <input type="submit" value="Загрузить всё">
</form>

Edit file php (редактирование php файла)

Обработчик upload_multi.php:

<?php
$uploadDir = 'uploads/';
foreach ($_FILES['files']['name'] as $key => $name) {
    $tmpName = $_FILES['files']['tmp_name'][$key];
    $error = $_FILES['files']['error'][$key];
    if ($error === UPLOAD_ERR_OK) {
        $filename = uniqid() . '_' . basename($name);
        $targetFile = $uploadDir . $filename;
        move_uploaded_file($tmpName, $targetFile);
        echo "Файл $name успешно загружен как $filename<br>";
    }
}
?>

Php управление файлами (управление файлами в php)

Цикл проходит по каждому загруженному файлу. Использование uniqid() снижает риск перезаписи существующих файлов и повышает безопасность.

Проблемы при загрузке нескольких файлов:

  • Ограничение количества файлов через настройки PHP не предусмотрено, но можно ограничить на стороне клиента или сервера.
  • Если один из файлов вызовет ошибку, остальные обработаются, если проверять ошибку отдельно.
  • При большом количестве файлов возможен выход за лимиты памяти или времени выполнения. Рекомендуется увеличить max_execution_time и memory_limit.

Как проверить тип и размер файла перед загрузкой?

Перед загрузкой необходимо проверять MIME-тип и размер, чтобы избежать вредоносных файлов. Используйте функцию finfo_file для определения реального типа содержимого, а не только расширения или заголовка Content-Type из формы, который может быть подделан. Пример проверки:

<?php
$allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
$maxSize = 2 * 1024 * 1024; // 2 МБ

$file = $_FILES['file'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);

if (!in_array($mimeType, $allowedTypes)) {
    die('Недопустимый тип файла.');
}
if ($file['size'] > $maxSize) {
    die('Размер файла превышает 2 МБ.');
}

$targetFile = 'uploads/' . basename($file['name']);
move_uploaded_file($file['tmp_name'], $targetFile);
?>

Php file get html (получение html файла через php)

Функция finfo_file использует файл magic.mgc, поэтому убедитесь, что расширение fileinfo включено в php.ini. Обратите внимание, что проверка размера через $_FILES['size'] производится на сервере, а не через скрытое поле формы.

Ошибки при проверке:

  • finfo_file может не сработать, если файл пуст или временный файл не существует. Проверяйте ошибку загрузки перед анализом.
  • MIME-тип может быть неоднозначным (например, application/octet-stream). В таких случаях ориентируйтесь на whitelist расширений в дополнение к MIME.
  • Не полагайтесь только на расширение файла: злоумышленник может переименовать опасный скрипт в image.jpg.

Как обработать ошибки загрузки файла?

Массив $_FILES содержит ключ 'error' с кодом ошибки. Все константы ошибок (UPLOAD_ERR_OK, UPLOAD_ERR_INI_SIZE и т.д.) можно использовать в условиях. Пример универсальной обработки:

<?php
$file = $_FILES['file'];
switch ($file['error']) {
    case UPLOAD_ERR_OK:
        // обработка загрузки
        break;
    case UPLOAD_ERR_INI_SIZE:
        echo 'Файл превышает максимальный размер, установленный в php.ini.';
        break;
    case UPLOAD_ERR_FORM_SIZE:
        echo 'Файл превышает максимальный размер, указанный в HTML-форме.';
        break;
    case UPLOAD_ERR_PARTIAL:
        echo 'Файл был загружен только частично.';
        break;
    case UPLOAD_ERR_NO_FILE:
        echo 'Файл не был выбран.';
        break;
    case UPLOAD_ERR_CANT_WRITE:
        echo 'Не удалось записать файл на диск.';
        break;
    default:
        echo 'Неизвестная ошибка загрузки.';
}
?>

Эту конструкцию стоит использовать всегда вместе с проверками типа и размера.

Частые проблемы при обработке ошибок:

  • Код ошибки UPLOAD_ERR_EXTENSION (значение 8) может появляться при отключении расширения PHP, но на практике встречается редко.
  • Если выводятся технические сообщения, не показывайте их пользователю. Логируйте ошибки и сообщайте общую информацию.
- Include system php (включение системного файла)
- Php index вопрос (вопрос по index.php)
- Php open file (открытие файла в php)

Расширенные примеры и нестандартные сценарии загрузки

Ниже приведены более сложные и редко встречающиеся примеры, которые могут потребоваться в реальных проектах.

Загрузка файлов с сохранением информации в базу данных

Иногда нужно не просто переместить файл, но и записать его метаданные (имя, размер, дату) в таблицу. Пример с использованием PDO:

Пример
<?php
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');

$file = $_FILES['file'];
$uploadDir = 'uploads/';
$filename = uniqid() . '_' . basename($file['name']);
$targetFile = $uploadDir . $filename;

if (move_uploaded_file($file['tmp_name'], $targetFile)) {
    $stmt = $db->prepare("INSERT INTO files (original_name, saved_name, size, mime_type, created_at) VALUES (?, ?, ?, ?, NOW())");
    $stmt->execute([
        $file['name'],
        $filename,
        $file['size'],
        mime_content_type($targetFile)
    ]);
    echo 'Файл загружен и запись добавлена.';
}
?>
Вывод: Файл загружен и запись добавлена.

В данном примере используется PDO для вставки данных. Функция mime_content_type получает MIME-тип уже сохранённого файла. Убедитесь, что в базе данных создана соответствующая таблица с полями.

Загрузка файлов с динамическим созданием поддиректорий по дате

Чтобы избежать засорения одной папки, файлы можно распределять по папкам на основе даты. Пример:

Пример
<?php
$baseDir = 'uploads/';
$dateDir = date('Y/m/d'); // например 2025/04/11
$targetDir = $baseDir . $dateDir;

if (!is_dir($targetDir)) {
    mkdir($targetDir, 0775, true);
}

$file = $_FILES['file'];
$filename = uniqid() . '_' . basename($file['name']);
$targetFile = $targetDir . '/' . $filename;

if (move_uploaded_file($file['tmp_name'], $targetFile)) {
    echo "Файл сохранён в $targetFile";
}
?>

Параметр true в mkdir рекурсивно создаёт все недостающие папки. Права 0775 дают владельцу полный доступ, а группе чтение и выполнение.

Загрузка файлов по частям (chunked upload) с поддержкой докачки

Для больших файлов применяют разбиение на части на клиенте (например, с помощью FileReader) и последовательную отправку. На серверной стороне части объединяются. Упрощённый пример для одного куска:

Пример
<?php
// Предполагаем, что клиент отправляет chunk с заголовками
$chunkIndex = $_POST['chunk'];
$totalChunks = $_POST['chunks'];
$filename = $_POST['name'];
$tempDir = 'temp/';
$tempFile = $tempDir . $filename . '.part';

$in = fopen($tempFile, 'ab');
fwrite($in, file_get_contents('php://input'));
fclose($in);

if ($chunkIndex == $totalChunks - 1) {
    rename($tempFile, 'uploads/' . $filename);
    echo 'Файл собран полностью.';
} else {
    echo 'Часть ' . ($chunkIndex + 1) . ' из ' . $totalChunks . ' получена.';
}
?>

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

Загрузка файлов с использованием потоковой передачи (PSR-7)

Современные PHP-фреймворки поддерживают PSR-7. Пример с использованием Slim Framework:

Пример
<?php
use Psr\Http\Message\ServerRequestInterface;
use Slim\Factory\AppFactory;

$app = AppFactory::create();

$app->post('/upload', function (ServerRequestInterface $request, $response) {
    $uploadedFiles = $request->getUploadedFiles();
    $uploadedFile = $uploadedFiles['file'];
    if ($uploadedFile->getError() === UPLOAD_ERR_OK) {
        $uploadedFile->moveTo('uploads/' . $uploadedFile->getClientFilename());
        $response->getBody()->write('Ok');
    }
    return $response;
});

$app->run();
?>

Объект UploadedFile из PSR-7 инкапсулирует работу с $_FILES и упрощает код.

Загрузка файла с параллельной проверкой антивирусом (ClamAV)

Для критичных проектов перед сохранением файла можно проверить его встроенным антивирусом. Пример через exec:

Пример
<?php
$file = $_FILES['file']['tmp_name'];
$scanOutput = shell_exec("clamscan --no-summary $file 2>&1");
if (strpos($scanOutput, 'OK') !== false) {
    move_uploaded_file($file, 'uploads/' . basename($_FILES['file']['name']));
    echo 'Файл чист и сохранён.';
} else {
    unlink($file);
    echo 'Обнаружена угроза! Файл удалён.';
}
?>
Если ClamAV установлен и настроен, вывод может быть:
Файл чист и сохранён.

Помните, что shell_exec может быть опасен, экранируйте аргументы. Альтернатива - использование библиотеки php-clamav.

Загрузка файлов на сервер с помощью PHP - comments

En
загрузки php (php)