Работа с файловыми потоками: чтение данных в PHP
В PHP чтение содержимого файла через потоки предоставляет гибкий контроль над процессом получения данных. Потоки позволяют работать с файлами, сетевыми соединениями и другими источниками данных единообразно. В этой части рассмотрены основные варианты чтения файлов с использованием потоковых функций, от простых до более сложных, с примерами кода и разбором типичных ошибок.
Основные подходы к чтению файлов через потоки
Самый эффективный способ: file_get_contents с контекстом потока
Функция file_get_contents позволяет прочитать весь файл в одну строку. При использовании контекста потока (stream_context_create) можно задать таймауты, методы HTTP, заголовки и другие параметры. Это оптимально для файлов, которые помещаются в оперативную память.
Цель: быстрое получение содержимого небольшого или среднего файла (до десятков мегабайт). Случаи использования: чтение конфигурационных файлов, ответов API, локальных текстовых файлов.
$context = stream_context_create([
'http' => [
'method' => 'GET',
'timeout' => 10
]
]);
$content = file_get_contents('https://example.com/data.txt', false, $context);
if ($content === false) {
// ошибка
}
echo $content;
Php failed to open stream (ошибка открытия потока в php (failed to open stream))
Проблема: при неудачном открытии возвращается false. Необходимо явно проверять результат. Для больших файлов может не хватить памяти - используйте поточное чтение.
Как читать файл частями, контролируя размер буфера?
Вариант: fopen + fread + fclose. Открывает поток, читает блоками заданного размера, закрывает. Используется для больших файлов, когда нужно обрабатывать данные по частям (например, при передаче файла или подсчете хеша).
$handle = fopen('largefile.txt', 'rb');
if ($handle) {
$bufferSize = 4096;
while (!feof($handle)) {
$chunk = fread($handle, $bufferSize);
// обработка $chunk
}
fclose($handle);
}
Stream file contents php (чтение содержимого файла через поток в php)
Ошибка: если не проверять fopen, скрипт может выдать предупреждение. Также fread может вернуть меньше байт, чем запрошено - это нормально, цикл продолжается до feof.
Как обработать большой файл строка за строкой?
Вариант: fgets. Читает одну строку (до символа новой строки). Подходит для построчной обработки логов, CSV и т.д.
$handle = fopen('log.txt', 'r');
if ($handle) {
while (($line = fgets($handle)) !== false) {
echo $line;
}
fclose($handle);
}
Проблема: fgets по умолчанию читает до 4096 байт, если строка длиннее, чтение будет разбито. Явно задайте длину вторым параметром, чтобы избежать разрывов.
Как использовать объектно-ориентированный подход для чтения файла?
Вариант: SplFileObject. Класс из SPL, реализует итератор для потока. Удобен для типовых операций, совместим с foreach.
$file = new SplFileObject('data.txt', 'r');
foreach ($file as $lineNum => $line) {
echo "Строка $lineNum: $line";
}
Ошибка: если файл не существует, выбрасывается исключение RuntimeException. Следует обрабатывать исключения или проверять существование файла.
Как получить содержимое произвольного потока (например, php://memory)?
Вариант: stream_get_contents. Функция читает остаток потока в строку. Используется после открытия любого потока (файл, сокет, временный поток).
$stream = fopen('php://memory', 'rb+');
fwrite($stream, 'Test data');
rewind($stream);
$content = stream_get_contents($stream);
fclose($stream);
echo $content;
Проблема: если указатель не на начале, прочитается только часть. Перед вызовом stream_get_contents следует перемотать поток с помощью rewind или fseek.
Как прочитать тело HTTP-запроса как поток?
Вариант: чтение из встроенного потока php://input. Используется для получения необработанных данных POST-запроса (например, JSON). Работает как поток только для чтения.
$rawData = file_get_contents('php://input');
$json = json_decode($rawData, true);
Ошибка: php://input доступен только для запросов с Content-Type application/x-www-form-urlencoded или multipart/form-data не для POST с типом multipart (в этом случае используйте $_POST). Для больших данных может быть недоступен при некоторых настройках сервера.
Расширенные примеры работы с файловыми потоками
В этом разделе приведены нестандартные сценарии использования потоков для чтения файлов, включая фильтрацию, прогресс-бары и пользовательские обработчики.
Пример 1. Чтение большого файла с отображением прогресса
Используется fopen + fread с выводом процента выполнения. Длина файла определяется через fstat.
$handle = fopen('bigfile.dat', 'rb');
if ($handle) {
$stat = fstat($handle);
$total = $stat['size'];
$readBytes = 0;
$bufferSize = 8192;
while (!feof($handle)) {
$data = fread($handle, $bufferSize);
$readBytes += strlen($data);
$percent = round($readBytes * 100 / $total);
echo "\rПрочитано: $percent%";
// обработка $data
}
fclose($handle);
echo "\nГотово.";
}
Прочитано: 45% Прочитано: 100% Готово.
Пример 2. Применение потокового фильтра (конвертация кодировки)
Чтение файла в UTF-8 с автоматической конвертацией из Windows-1251 с помощью фильтра convert.iconv.
$handle = fopen('file_win1251.txt', 'rb');
stream_filter_append($handle, 'convert.iconv.windows-1251/utf-8');
while (($line = fgets($handle)) !== false) {
echo $line;
}
fclose($handle);
(Строки в UTF-8)
Пример 3. Чтение удаленного файла через HTTP с обработкой редиректов
Используется stream_context_create с параметрами follow_location и max_redirects.
$options = [
'http' => [
'follow_location' => 1,
'max_redirects' => 5,
'timeout' => 30
]
];
$context = stream_context_create($options);
$content = file_get_contents('http://example.com/redirecting-file', false, $context);
if ($content !== false) {
echo 'Длина: ' . strlen($content) . ' байт';
}
Длина: 12345 байт
Пример 4. Чтение из пользовательского потока (создание обертки streamWrapper)
Регистрация собственной обертки для чтения данных из базы данных как потока. Упрощенная реализация.
class DBStream {
protected $data;
protected $position;
public function stream_open($path, $mode, $options, &$opened_path) {
// извлекаем данные из БД
$this->data = 'Data from database';
$this->position = 0;
return true;
}
public function stream_read($count) {
$chunk = substr($this->data, $this->position, $count);
$this->position += strlen($chunk);
return $chunk;
}
public function stream_eof() {
return $this->position >= strlen($this->data);
}
public function stream_stat() {
return ['size' => strlen($this->data)];
}
}
stream_wrapper_register('db', 'DBStream');
$content = file_get_contents('db://query');
echo $content;
Data from database
Пример 5. Комбинированное чтение: частичное с пропуском байтов
Чтение 100 байт, начиная с 50 позиции, с помощью fseek и fread.
$handle = fopen('file.txt', 'rb');
fseek($handle, 50);
$chunk = fread($handle, 100);
echo $chunk;
(Символы с 51 по 150)
Пример 6. Использование stream_copy_to_stream для копирования между потоками
Копирование содержимого одного файла в другой через потоки без загрузки в память.
$src = fopen('source.txt', 'rb');
$dst = fopen('destination.txt', 'wb');
$copiedBytes = stream_copy_to_stream($src, $dst);
fclose($src);
fclose($dst);
echo "Скопировано $copiedBytes байт";
Скопировано 2048 байт