Чтение и отображение файлов в PHP
Основные подходы к просмотру файлов в PHP
Наиболее эффективное решение: функция file_get_contents с проверками безопасности
Этот способ подходит для небольших и средних файлов (до нескольких десятков мегабайт). Он выполняет чтение всего содержимого в строку за один вызов, что удобно для последующей обработки или вывода.
$file = '/var/www/uploads/example.txt';
$allowedDir = '/var/www/uploads/';
// Проверка, что файл находится внутри разрешённой директории
if (strpos(realpath($file), realpath($allowedDir)) !== 0) {
die('Доступ запрещён');
}
// Проверка существования и прав на чтение
if (!file_exists($file) || !is_readable($file)) {
die('Файл не найден или недоступен для чтения');
}
// Чтение и экранирование для безопасного вывода в HTML
$content = file_get_contents($file);
if ($content === false) {
die('Ошибка при чтении файла');
}
echo htmlspecialchars($content, ENT_QUOTES, 'UTF-8');
Php file viewer (просмотр файла в php)
Возможные проблемы и их решение
- Большие файлы: file_get_contents загружает весь файл в память. Для файлов размером более 100 МБ может не хватить памяти. Решение: использовать потоковое чтение (fread с буфером).
- Бинарные файлы: при выводе бинарных данных (изображения, PDF) нельзя применять htmlspecialchars. Необходимо выставлять правильный Content-Type и использовать readfile.
- Путь traversal: если имя файла приходит из пользовательского ввода, обязательно проверять, что конечный путь находится в разрешённой директории (через realpath).
Вариант 1. Как прочитать файл и вывести его без загрузки в память целиком?
Использовать функцию readfile - она читает файл и сразу отправляет его в выходной поток, не создавая промежуточной строки. Идеально для больших файлов или прямого скачивания.
$file = 'large_file.zip';
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Content-Length: ' . filesize($file));
readfile($file);
Php resource file (ресурсный файл в php)
Проблема: при ошибке чтения readfile возвращает количество прочитанных байт, но не выбрасывает исключение. Нужно проверять результат: if (readfile($file) === false) { /* обработка ошибки */ }
Вариант 2. Как гарантировать безопасное отображение текстовых файлов с различными кодировками?
При чтении текстовых файлов с неизвестной кодировкой возможны артефакты. Используйте mb_detect_encoding для определения кодировки и конвертации в UTF-8 перед выводом.
$content = file_get_contents($file);
$enc = mb_detect_encoding($content, ['UTF-8', 'Windows-1251', 'KOI8-R'], true);
if ($enc !== 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $enc);
}
echo htmlspecialchars($content);
Некорректное определение кодировки может привести к потере данных. Для критичных случаев лучше явно указывать ожидаемую кодировку.
Вариант 3. Как вывести изображение или PDF в браузере через PHP?
Для вывода медиаконтента необходимо передать правильный MIME-тип и вывести бинарные данные через readfile (или fread). Пример для изображения PNG:
$file = 'photo.png';
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file);
finfo_close($finfo);
header('Content-Type: ' . $mimeType);
header('Content-Length: ' . filesize($file));
readfile($file);
Проблема: если файл повреждён или не является изображением, браузер может показать ошибку. Добавьте проверку MIME-типа: if (!in_array($mimeType, ['image/png', 'image/jpeg'])) { die('Недопустимый тип файла'); }
Вариант 4. Как читать файл построчно для логов больших размеров?
Для логов (например, размером 1 ГБ) используйте fopen + fgets в цикле. Это позволяет обрабатывать строку за строкой, не загружая файл целиком.
$handle = fopen('server.log', 'r');
if ($handle) {
while (($line = fgets($handle)) !== false) {
// обработка строки
echo htmlspecialchars($line) . "<br>";
}
fclose($handle);
} else {
die('Не удалось открыть файл');
}
Важно закрыть файл через fclose, иначе может быть исчерпан лимит открытых дескрипторов. Также проверяйте кодировку каждой строки.
Расширенные примеры просмотра файлов
Пример 1. Потоковое чтение и вывод бинарного файла с буферизацией
Для очень больших файлов (например, видео) лучше использовать собственный буфер, чтобы не перегружать память. Функция fread позволяет читать кусками по 1 МБ.
function streamFile($filePath, $chunkSize = 1048576) {
$handle = fopen($filePath, 'rb');
if ($handle === false) {
throw new RuntimeException('Cannot open file');
}
while (!feof($handle)) {
$chunk = fread($handle, $chunkSize);
if ($chunk === false) {
fclose($handle);
throw new RuntimeException('Read error');
}
echo $chunk;
ob_flush();
flush();
}
fclose($handle);
}
$file = 'large_video.mp4';
header('Content-Type: video/mp4');
header('Content-Length: ' . filesize($file));
streamFile($file);
Результат: видео проигрывается в браузере без полной загрузки в память PHP.
Пример 2. Просмотр файла с авторизацией и ограничением доступа по IP
Если требуется дать доступ к файлам только определённым пользователям, можно реализовать собственную логику. В примере файл отдаётся только после проверки сессии.
session_start();
if (!isset($_SESSION['user'])) {
http_response_code(403);
die('Доступ запрещён');
}
$file = '/srv/private/' . basename($_GET['file']);
$allowedDir = '/srv/private/';
$realPath = realpath($file);
if ($realPath === false || strpos($realPath, realpath($allowedDir)) !== 0) {
http_response_code(404);
die('Файл не найден');
}
$mimeType = mime_content_type($file);
header('Content-Type: ' . $mimeType);
header('Content-Disposition: inline; filename="' . basename($file) . '"');
readfile($file);
Результат: файл открывается в браузере только после успешной аутентификации.
Пример 3. Использование SplFileObject для построчного чтения и фильтрации
SplFileObject предоставляет объектно-ориентированный интерфейс для работы с файлами. Удобно для обработки CSV или логов.
$file = new SplFileObject('data.csv');
$file->setFlags(SplFileObject::READ_CSV);
$output = [];
foreach ($file as $row) {
// Фильтрация строк по условию
if (isset($row[0]) && $row[0] === 'active') {
$output[] = $row;
}
}
echo '<pre>' . print_r($output, true) . '</pre>';
Array
(
[0] => Array
(
[0] => active
[1] => user1
[2] => value1
)
)
Пример 4. Чтение удалённого файла через cURL с таймаутом
Для файлов, расположенных на внешних серверах, можно использовать cURL. Он даёт больше контроля (таймауты, заголовки).
$url = 'https://example.com/remote.txt';
$timeout = 5;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$content = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || $content === false) {
die('Ошибка при загрузке удалённого файла');
}
echo htmlspecialchars($content);
Результат: содержимое файла отображается в браузере (при успешном ответе 200).
Пример 5. Просмотр файла с определением MIME-типа через finfo и чанкованный вывод
Комбинация двух подходов: определение типа через magic bytes (без расширения) и потоковая передача.
$file = 'unknown.bin';
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file);
header('Content-Type: ' . $mime);
header('Content-Length: ' . filesize($file));
$handle = fopen($file, 'rb');
if ($handle) {
while (!feof($handle)) {
echo fread($handle, 4096);
}
fclose($handle);
}
Результат: браузер интерпретирует файл согласно его реальному типу (например, image/png).