Прием файлов от пользователя в PHP проектах
Основной метод: формы и обработчик
Самый распространенный способ загрузки файлов в PHP - использование HTML формы с атрибутом enctype="multipart/form-data" и серверного скрипта, который читает суперглобальный массив $_FILES. Далее файл перемещается из временной папки в постоянное расположение функцией move_uploaded_file().
Как организовать базовую загрузку одного файла с проверкой ошибок?
<!-- upload.html -->
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="userfile">
<input type="submit" value="Загрузить">
</form>
<?php // upload.php
$targetDir = __DIR__ . '/uploads/';
if (!is_dir($targetDir)) {
mkdir($targetDir, 0755, true);
}
$file = $_FILES['userfile'] ?? null;
if ($file && $file['error'] === UPLOAD_ERR_OK) {
$tmpPath = $file['tmp_name'];
$name = basename($file['name']);
$destPath = $targetDir . $name;
if (move_uploaded_file($tmpPath, $destPath)) {
echo "Файл сохранен: " . htmlspecialchars($name);
} else {
echo "Ошибка перемещения файла.";
}
} else {
$errorCode = $file['error'] ?? UPLOAD_ERR_NO_FILE;
echo "Ошибка загрузки: " . $errorCode;
}
?>
Index php path (пути файлов в php)
Пояснение: форма передает файл на сервер, PHP сохраняет его во временной директории. Скрипт проверяет наличие ошибки, используя константы UPLOAD_ERR_OK и другие. Затем перемещает файл в папку uploads. В реальных проектах необходимо дополнительно проверять размер и тип файла, а также генерировать уникальное имя для предотвращения перезаписи.
Типичные проблемы:
- Файл не загружается, если превышен лимит upload_max_filesize в php.ini. Решение: увеличить лимит и/или обрабатывать ошибку UPLOAD_ERR_INI_SIZE.
- Недостаточно прав на запись в папку назначения. Решение: проверить права (например, chmod 755).
- Имена файлов могут содержать некорректные символы. Решение: использовать basename() и генерировать безопасное имя.
- Файл может оказаться слишком большим для POST запроса из-за post_max_size. Решение: настроить параметры сервера.
Как разрешить загрузку нескольких файлов одновременно?
Используйте атрибут multiple в теге input и добавьте квадратные скобки в имя поля (например, name="files[]"). На сервере массив $_FILES['files'] будет содержать информацию по каждому файлу.
<form action="upload_multi.php" method="post" enctype="multipart/form-data">
<input type="file" name="files[]" multiple>
<input type="submit" value="Загрузить все">
</form>
<?php
$targetDir = __DIR__ . '/uploads/';
if (!is_dir($targetDir)) mkdir($targetDir, 0755, true);
if (!empty($_FILES['files']['name'][0])) {
foreach ($_FILES['files']['name'] as $i => $name) {
if ($_FILES['files']['error'][$i] === UPLOAD_ERR_OK) {
$tmpPath = $_FILES['files']['tmp_name'][$i];
$safeName = uniqid() . '-' . basename($name);
move_uploaded_file($tmpPath, $targetDir . $safeName);
echo "Сохранен: " . htmlspecialchars($safeName) . "<br>";
}
}
}
?>
Php tmp files (временные файлы php)
Важно: безопасно обрабатывать каждое имя, чтобы избежать атак. Для массовой загрузки также следует учитывать суммарный размер файлов, который может превысить post_max_size.
Как загружать файлы размером больше лимитов PHP?
Одно из решений - разбить файл на части на клиенте (JavaScript) и отправлять их последовательно или параллельно на сервер, где каждая часть дописывается к результирующему файлу. Это обходит ограничения upload_max_filesize и post_max_size. Ниже упрощенный серверный обработчик для приема чанков.
<?php // chunk_upload.php
$targetFile = __DIR__ . '/uploads/' . ($_POST['filename'] ?? 'chunk');
$chunkIndex = intval($_POST['chunk'] ?? 0);
$totalChunks = intval($_POST['chunks'] ?? 1);
$data = file_get_contents('php://input');
$mode = ($chunkIndex === 0) ? 'wb' : 'ab';
$fp = fopen($targetFile, $mode);
if ($fp) {
fwrite($fp, $data);
fclose($fp);
}
if ($chunkIndex === $totalChunks - 1) {
echo "Загрузка завершена";
} else {
echo "Чанк " . ($chunkIndex+1) . " из $totalChunks принят";
}
?>
Edit file php (редактирование php файла)
В реальном проекте необходимо добавить проверку токена, сессии, а также контрольную сумму для каждого чанка. На клиенте используется File.slice() и отправка через XHR.
Как реализовать загрузку без перезагрузки страницы?
Используйте объект FormData и XMLHttpRequest (или fetch). Серверный скрипт остается таким же, как при обычной загрузке. Клиентский код отправляет файл асинхронно.
<!-- HTML -->
<input type="file" id="fileInput">
<button onclick="upload()">Загрузить</button>
<div id="status"></div>
<script>
function upload() {
var file = document.getElementById('fileInput').files[0];
var formData = new FormData();
formData.append('userfile', file);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.php', true);
xhr.onload = function() {
if (xhr.status === 200) {
document.getElementById('status').innerText = xhr.responseText;
}
};
xhr.send(formData);
}
</script>
Php file get html (получение html файла через php)
Серверный скрипт (upload.php) может возвращать JSON с результатом. Для отслеживания прогресса используйте событие xhr.upload.onprogress.
Как загружать изображения, проверять их тип и создавать превью?
Для надежной проверки типа изображения используйте функцию finfo или getimagesize. Создание миниатюр требует библиотеки GD или Imagick. Пример ниже использует GD для ресайза.
<?php
if ($_FILES['image']['error'] === UPLOAD_ERR_OK) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['image']['tmp_name']);
finfo_close($finfo);
$allowed = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mime, $allowed)) {
die('Недопустимый тип файла');
}
// Создание миниатюры
list($origW, $origH) = getimagesize($_FILES['image']['tmp_name']);
$maxSize = 200;
$ratio = $origW / $origH;
if ($origW > $origH) {
$newW = $maxSize;
$newH = $maxSize / $ratio;
} else {
$newH = $maxSize;
$newW = $maxSize * $ratio;
}
$thumb = imagecreatetruecolor($newW, $newH);
$src = imagecreatefromstring(file_get_contents($_FILES['image']['tmp_name']));
imagecopyresampled($thumb, $src, 0, 0, 0, 0, $newW, $newH, $origW, $origH);
imagejpeg($thumb, __DIR__ . '/uploads/thumb_' . uniqid() . '.jpg', 80);
imagedestroy($thumb);
imagedestroy($src);
echo 'Миниатюра создана';
}
?>
Root php (корневая директория сервера в php)
Важно: не полагаться только на расширение файла - mime-тип может быть подменен. Используйте проверку через содержимое.
Как предотвратить перезапись файлов и атаки через имя?
Генерируйте уникальное имя, используя uniqid(), hash_file() или комбинацию с временной меткой. Удалите потенциально опасные символы из оригинального имени или полностью замените его.
<?php
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$safeName = md5(uniqid() . microtime()) . '.' . $ext;
// Либо
$hash = hash_file('sha256', $_FILES['file']['tmp_name']);
$safeName = $hash . '.' . $ext;
move_uploaded_file($_FILES['file']['tmp_name'], __DIR__ . '/uploads/' . $safeName);
?>
Дополнительно сохраняйте файлы вне document_root, если нет необходимости отдавать их напрямую, и используйте специальный скрипт для выдачи.
Расширенные сценарии загрузки
Загрузка через потоковый ввод (php://input)
Если файл передается в теле запроса (например, PUT запрос или application/octet-stream), можно прочитать данные напрямую из потока.
<?php
// put_upload.php - ожидает raw data в теле запроса
$filename = $_SERVER['HTTP_X_FILENAME'] ?? 'unknown.bin';
$input = fopen('php://input', 'rb');
$dest = fopen(__DIR__ . '/uploads/' . basename($filename), 'wb');
stream_copy_to_stream($input, $dest);
fclose($input);
fclose($dest);
echo "Файл $filename сохранен";
?>
Этот подход полезен для API, где клиент отправляет файл без формы.
Интеграция с облачным хранилищем (S3)
Используя библиотеку league/flysystem, можно абстрагироваться от локальной файловой системы. Пример сохранения файла в S3.
<?php
require 'vendor/autoload.php';
use League\Flysystem\Filesystem;
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
use Aws\S3\S3Client;
$client = new S3Client([
'version' => 'latest',
'region' => 'us-east-1',
'credentials' => [
'key' => 'YOUR_KEY',
'secret' => 'YOUR_SECRET',
],
]);
$adapter = new AwsS3V3Adapter($client, 'bucket-name');
$filesystem = new Filesystem($adapter);
$file = $_FILES['userfile'];
if ($file['error'] === UPLOAD_ERR_OK) {
$stream = fopen($file['tmp_name'], 'r');
$filesystem->writeStream('uploads/' . $file['name'], $stream);
fclose($stream);
echo 'Файл загружен в S3';
}
?>
Результат: файл физически хранится в облаке, что обеспечивает масштабируемость.
Сканирование загруженных файлов антивирусом ClamAV
Подключитесь к демону ClamAV через сокет и проверьте загруженный файл перед сохранением.
<?php
if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
$tmpPath = $_FILES['file']['tmp_name'];
$clamSocket = fopen('unix:///var/run/clamav/clamd.sock', 'r+');
if ($clamSocket) {
fwrite($clamSocket, "SCAN $tmpPath\n");
$response = fgets($clamSocket);
fclose($clamSocket);
if (strpos($response, 'OK') !== false) {
move_uploaded_file($tmpPath, 'uploads/' . basename($_FILES['file']['name']));
echo 'Файл безопасен и сохранен';
} else {
unlink($tmpPath);
echo 'Файл содержит вирус: ' . htmlspecialchars($response);
}
}
}
?>
Пример ответа: "/tmp/phpXXXX: OK"
Важно: демон ClamAV должен быть установлен и запущен на сервере.
Загрузка CSV с последующей вставкой данных в MySQL
Пользователь загружает CSV файл, PHP читает его и добавляет строки в таблицу.
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
if ($_FILES['csv']['error'] === UPLOAD_ERR_OK) {
$handle = fopen($_FILES['csv']['tmp_name'], 'r');
$header = fgetcsv($handle); // первая строка - заголовки
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
while (($row = fgetcsv($handle)) !== false) {
$stmt->execute([$row[0], $row[1]]);
}
fclose($handle);
echo 'CSV импортирован, строк: ' . $stmt->rowCount();
}
?>
CSV импортирован, строк: 150
Рекомендуется проверять количество столбцов и экранировать данные.