Чтение и отображение файлов в PHP

Раздел: Управление файлами в 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).

Просмотр файла в PHP - comments

En
Php file viewer (php)