Реализация загрузки файлов средствами PHP: от простого к сложному

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

Основной способ загрузки файла через суперглобальный массив $_FILES

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

Наиболее распространённый метод - использование элемента формы input type="file" и обработчика на PHP, который получает данные из массива $_FILES.

Пример формы (файл upload.html):


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

Php file date (дата файла в php)

Пример обработчика (файл upload.php):


<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $uploadDir = __DIR__ . '/uploads/';
    if (!is_dir($uploadDir)) {
        mkdir($uploadDir, 0755, true);
    }
    $tmpName = $_FILES['userfile']['tmp_name'];
    $fileName = basename($_FILES['userfile']['name']);
    if (move_uploaded_file($tmpName, $uploadDir . $fileName)) {
        echo "Файл '$fileName' загружен успешно.";
    } else {
        echo "Ошибка при перемещении файла.";
    }
}
?>
  

Php загрузка файла (загрузка файла в php)

Типичные проблемы:

  • Файл не загружается (пустой $_FILES). Причина: не указан атрибут enctype="multipart/form-data" в форме.
  • Ошибка «Не удалось переместить файл». Проверить права на папку назначения и её существование.
  • Превышение лимита upload_max_filesize. Настроить php.ini или использовать .htaccess.

Варианты реализации загрузки

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

Используется массив в имени поля формы, например name="files[]". В PHP каждый файл будет доступен как элемент массива $_FILES['files'].


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

обработка файлов php (обработка файлов в php)


<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $uploadDir = __DIR__ . '/uploads/';
    foreach ($_FILES['files']['tmp_name'] as $key => $tmpName) {
        if ($_FILES['files']['error'][$key] === UPLOAD_ERR_OK) {
            $name = basename($_FILES['files']['name'][$key]);
            move_uploaded_file($tmpName, $uploadDir . $name);
        }
    }
}
?>
  

Files show php file (показ файлов в php)

Как реализовать асинхронную загрузку через AJAX?

Для отправки файла без перезагрузки страницы используется объект FormData и fetch/XMLHttpRequest.


// frontend (JavaScript)
const form = document.getElementById('uploadForm');
form.addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData(form);
    const response = await fetch('ajax_upload.php', { method: 'POST', body: formData });
    const result = await response.text();
    console.log(result);
});
  

прочитать php (чтение данных в php)


<?php // ajax_upload.php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $uploadDir = __DIR__ . '/uploads/';
    $tmp = $_FILES['file']['tmp_name'];
    $name = basename($_FILES['file']['name']);
    if (move_uploaded_file($tmp, $uploadDir . $name)) {
        echo "Файл $name отправлен асинхронно.";
    } else {
        http_response_code(500);
        echo "Ошибка загрузки.";
    }
}
?>
  

Php get file type (определение типа файла в php)

Как обеспечить безопасную загрузку с проверкой MIME-типа и расширения?

Используется комбинация mime_content_type() или finfo для проверки типа и проверка расширения файла.


$allowedMime = ['image/jpeg', 'image/png', 'application/pdf'];
$allowedExt = ['jpg', 'jpeg', 'png', 'pdf'];

$fileInfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($fileInfo, $_FILES['file']['tmp_name']);
finfo_close($fileInfo);

$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));

if (!in_array($mime, $allowedMime) || !in_array($ext, $allowedExt)) {
    die("Недопустимый тип файла.");
}
  

Потенциальные ошибки при валидации:

  • MIME-тип не совпадает с расширением. Злоумышленник может переименовать .exe в .jpg. Рекомендуется проверять именно mime-тип содержимого.
  • Переполнение буфера при очень большом файле. Установить лимит через upload_max_filesize и post_max_size.

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

Генерация уникального имени через uniqid() или md5(time()).


$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$newName = uniqid('upload_', true) . '.' . $ext;
$destination = __DIR__ . '/uploads/' . $newName;
move_uploaded_file($_FILES['file']['tmp_name'], $destination);
  

Расширенные примеры с кодом и результатом

Пример 1: загрузка с проверкой размера и созданием миниатюры для изображений

Пример

<?php
// image_upload.php
$maxSize = 2 * 1024 * 1024; // 2 MB
$uploadDir = __DIR__ . '/uploads/';

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
    if ($_FILES['image']['error'] !== UPLOAD_ERR_OK) {
        die("Ошибка загрузки.");
    }
    if ($_FILES['image']['size'] > $maxSize) {
        die("Файл слишком большой.");
    }
    $tmp = $_FILES['image']['tmp_name'];
    $ext = strtolower(pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION));
    $newName = 'img_' . uniqid() . '.' . $ext;
    $dest = $uploadDir . $newName;
    if (move_uploaded_file($tmp, $dest)) {
        // Создание миниатюры (только для JPEG)
        if (in_array($ext, ['jpg', 'jpeg'])) {
            $src = imagecreatefromjpeg($dest);
            $thumb = imagescale($src, 150, 150);
            $thumbName = 'thumb_' . $newName;
            imagejpeg($thumb, $uploadDir . $thumbName, 80);
            imagedestroy($src);
            imagedestroy($thumb);
            echo "Файл загружен: $newName
"; echo "Миниатюра: $thumbName"; } else { echo "Файл загружен: $newName"; } } else { echo "Не удалось сохранить файл."; } } ?>

Результат (вывод при успешной загрузке):

Файл загружен: img_64f2a1b3c0d5e.jpg
Миниатюра: thumb_img_64f2a1b3c0d5e.jpg

Пример 2: Загрузка с использованием потоковой обработки (для больших файлов)

Пример

<?php
// stream_upload.php
$input = fopen("php://input", "rb");
$dest = fopen(__DIR__ . '/uploads/stream_' . date('Ymd_His') . '.dat', 'wb');
stream_copy_to_stream($input, $dest);
fclose($input);
fclose($dest);
echo "Файл принят через поток.";
?>

Примечание: этот вариант не использует стандартную форму, а получает сырые данные, например, при отправке через fetch с Content-Type: application/octet-stream.

Пример 3: Обработка ошибок загрузки с переводом кодов ошибок

Пример

<?php
$errorMessages = [
    UPLOAD_ERR_OK => 'Загрузка успешна',
    UPLOAD_ERR_INI_SIZE => 'Размер файла превышает upload_max_filesize',
    UPLOAD_ERR_FORM_SIZE => 'Размер файла превышает MAX_FILE_SIZE в форме',
    UPLOAD_ERR_PARTIAL => 'Файл загружен частично',
    UPLOAD_ERR_NO_FILE => 'Файл не выбран',
    UPLOAD_ERR_NO_TMP_DIR => 'Отсутствует временная папка',
    UPLOAD_ERR_CANT_WRITE => 'Ошибка записи на диск',
    UPLOAD_ERR_EXTENSION => 'Загрузка остановлена расширением PHP',
];

$errorCode = $_FILES['file']['error'] ?? UPLOAD_ERR_NO_FILE;
echo $errorMessages[$errorCode] ?? 'Неизвестная ошибка';
?>

Результат (если файл не выбран):

Файл не выбран

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

Пример

<?php
$uploadDir = __DIR__ . '/uploads/';
$baseName = 'document';
$ext = 'pdf';
$counter = 1;
$fileName = "$baseName.$ext";
while (file_exists($uploadDir . $fileName)) {
    $fileName = $baseName . '_' . $counter . '.' . $ext;
    $counter++;
}
move_uploaded_file($_FILES['file']['tmp_name'], $uploadDir . $fileName);
echo "Сохранили как $fileName";
?>

Результат (если уже есть document.pdf):

Сохранили как document_1.pdf

Загрузка файла в PHP - comments

En
Php загрузка файла (php)