Zip entry read: примеры (PHP)

Чтение содержимого записей ZIP в PHP через zip_entry_read
Раздел: Архивация
zip_entry_read(resource zip_entry, int length): string|false

Описание функции zip_entry_read

Назначение и общая информация

Функция zip_entry_read() используется для чтения содержимого конкретной записи (файла) внутри открытого ZIP-архива. Она является частью старого, основанного на ресурсах расширения Zip (до PHP 8.0.0) и сейчас считается устаревшей.

Аргументы функции
  • zip_entry (resource) - обязательный. Ресурс записи ZIP, полученный с помощью zip_read() или zip_entry_open().
  • length (int) - необязательный. Количество байтов для чтения из записи. Если параметр не указан или равен 0, функция пытается прочитать 1024 байта. Фактическое количество прочитанных байтов может быть меньше запрошенного, особенно в конце файла.
Возвращаемое значение

Функция возвращает прочитанные данные в виде строки. Возвращается пустая строка ('') при достижении конца файла (EOF). В случае возникновения ошибки возвращается false.

Базовые примеры использования

Пример 1: Чтение всего содержимого файла по частям
<?
$zip = zip_open('archive.zip');
if ($zip) {
    while ($entry = zip_read($zip)) {
        if (zip_entry_open($zip, $entry, 'r')) {
            echo 'Чтение: ' . zip_entry_name($entry) . '\n';
            $content = '';
            while ($data = zip_entry_read($entry, 2048)) {
                $content .= $data;
            }
            echo 'Размер содержимого: ' . strlen($content) . ' байт\n';
            zip_entry_close($entry);
        }
    }
    zip_close($zip);
}
?>
Чтение: documents/report.txt
Размер содержимого: 1256 байт
Чтение: images/photo.jpg
Размер содержимого: 54321 байт
Пример 2: Чтение с указанием размера блока
<?
$zip = zip_open('data.zip');
$entry = zip_read($zip);
zip_entry_open($zip, $entry, 'r');

// Чтение первых 100 байт
$firstChunk = zip_entry_read($entry, 100);
echo 'Первые 100 байт (длина): ' . strlen($firstChunk) . '\n';

// Чтение следующих 500 байт
$secondChunk = zip_entry_read($entry, 500);
echo 'Следующие 500 байт (длина): ' . strlen($secondChunk) . '\n';

zip_entry_close($entry);
zip_close($zip);
?>
Первые 100 байт (длина): 100
Следующие 500 байт (длина): 500

Альтернативы в современном PHP

Класс ZipArchive (рекомендуется)

Класс ZipArchive (расширение ext-zip) является объектно-ориентированной и более мощной альтернативой. Он предоставляет методы getFromName() и getFromIndex() для чтения всего содержимого файла целиком, а также возможность работы с потоковым чтением через fopen() и fread() с использованием оберток zip://.

$zip = new ZipArchive;
if ($zip->open('archive.zip') === TRUE) {
    $content = $zip->getFromName('file.txt'); // Прочитать весь файл
    $stream = $zip->getStream('file.txt');    // Получить поток для чтения
    fread($stream, 1024); // Читать из потока
    fclose($stream);
    $zip->close();
}
Потоковые обертки zip://

Позволяют работать с файлами внутри архива как с обычными файлами, используя стандартные функции вроде fopen(), fgets(), stream_get_contents().

$handle = fopen('zip://archive.zip#file.txt', 'r');
$content = stream_get_contents($handle);
fclose($handle);

Предпочтения: Для нового кода всегда следует использовать ZipArchive или потоковые обертки. Старые функции на основе ресурсов (zip_open, zip_entry_read) устарели в PHP 8.0.0 и удалены в PHP 8.1.0.

Аналоги в других языках программирования

Python: модуль zipfile

Класс ZipFile предоставляет метод read() для получения байтового содержимого файла по имени.

import zipfile
with zipfile.ZipFile('archive.zip', 'r') as zf:
    content = zf.read('file.txt')  # Возвращает bytes
    print(content.decode('utf-8'))
JavaScript (Node.js): модуль adm-zip

Сторонний модуль adm-zip позволяет читать содержимое записей.

const AdmZip = require('adm-zip');
const zip = new AdmZip('archive.zip');
const entry = zip.getEntry('file.txt');
const content = entry.getData().toString('utf8');
console.log(content);
Bash: unzip с выводом в stdout

Утилита командной строки unzip может извлекать конкретный файл в стандартный вывод.

unzip -p archive.zip file.txt

Отличия: В отличие от итеративного чтения в zip_entry_read(), аналоги в Python и JS часто читают файл целиком в память. Подход, основанный на потоках, в Node.js (yauzl) более схож с поэтапным чтением.

Типичные ошибки и их решение

Ошибка 1: Чтение без открытия записи
<?
$zip = zip_open('archive.zip');
$entry = zip_read($zip);
// Забыли вызвать zip_entry_open
$data = zip_entry_read($entry); // Предупреждение и false
?>
Warning: zip_entry_read(): supplied resource is not a valid Zip Entry resource

Решение: Всегда открывать запись с помощью zip_entry_open() перед чтением.

Ошибка 2: Попытка чтения после достижения EOF
while ($data = zip_entry_read($entry, 1024)) {
    // Обработка данных
}
// Цикл завершился, потому что $data стала false или пустой строкой
$extraRead = zip_entry_read($entry, 1024); // false
if ($extraRead === false) {
    echo 'Достигнут конец файла или ошибка';
}

Решение: Проверять возвращаемое значение. Пустая строка означает EOF, false - ошибку. После EOF дальнейшие вызовы возвращают пустую строку.

Ошибка 3: Неправильная обработка бинарных данных
// Если файл содержит бинарные данные (например, изображение),
// его нельзя выводить как текст или использовать в строковых операциях
// без специальной обработки (например, кодирования в base64).
$imageData = '';
while ($chunk = zip_entry_read($entry)) {
    $imageData .= $chunk;
}
// $imageData содержит бинарную строку
header('Content-Type: image/jpeg');
echo $imageData; // Корректный вывод изображения

История изменений функции

  • В PHP 8.0.0 все функции модуля Zip, основанные на ресурсах (включая zip_entry_read), были переведены в разряд устаревших (deprecated). При их использовании генерировалось уведомление об устаревании (E_DEPRECATED).
  • В PHP 8.1.0 эти устаревшие функции были полностью удалены из ядра языка. Любые попытки их вызова приводят к фатальной ошибке.

Таким образом, в актуальных версиях PHP (8.1 и выше) функция zip_entry_read() не существует. Для работы с ZIP-архивами требуется использование класса ZipArchive.

Расширенные и специфические примеры

Пример 1: Построчное чтение текстового файла (если позволяет размер буфера)

Чтение файла построчно, предполагая, что строка помещается в один блок чтения. В реальности это ненадежно для длинных строк.

Пример php
<?
$zip = zip_open('logs.zip');
if ($zip) {
    while ($entry = zip_read($zip)) {
        if (zip_entry_open($zip, $entry) && strpos(zip_entry_name($entry), '.log')) {
            echo '--- ' . zip_entry_name($entry) . ' ---\n';
            $buffer = '';
            while (($data = zip_entry_read($entry, 1024)) !== '') {
                $buffer .= $data;
                $lines = explode("\n", $buffer);
                // Обрабатываем все, кроме последней, возможно неполной строки
                $buffer = array_pop($lines);
                foreach ($lines as $line) {
                    echo 'Строка: ' . htmlspecialchars($line) . '\n';
                }
            }
            // Вывод последней строки из буфера
            if ($buffer !== '') {
                echo 'Строка: ' . htmlspecialchars($buffer) . '\n';
            }
            zip_entry_close($entry);
        }
    }
    zip_close($zip);
}
?>
Пример 2: Сравнение времени чтения разными размерами блоков
Пример php
<?
function readWithChunkSize($zipName, $chunkSize) {
    $start = microtime(true);
    $zip = zip_open($zipName);
    $entry = zip_read($zip);
    zip_entry_open($zip, $entry);
    $totalBytes = 0;
    while ($data = zip_entry_read($entry, $chunkSize)) {
        $totalBytes += strlen($data);
    }
    zip_entry_close($entry);
    zip_close($zip);
    $time = microtime(true) - $start;
    return ['size' => $chunkSize, 'time' => $time, 'bytes' => $totalBytes];
}

$sizes = [128, 1024, 8192, 65536];
foreach ($sizes as $size) {
    $res = readWithChunkSize('largefile.zip', $size);
    printf("Блок %6d байт: прочитано %d байт за %.4f сек\n",
           $res['size'], $res['bytes'], $res['time']);
}
?>
Блок    128 байт: прочитано 1048576 байт за 0.1234 сек
Блок   1024 байт: прочитано 1048576 байт за 0.0456 сек
Блок   8192 байт: прочитано 1048576 байт за 0.0234 сек
Блок  65536 байт: прочитано 1048576 байт за 0.0189 сек
Пример 3: Чтение и немедленная передача в выходной поток (streaming)

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

Пример php
<?
$zip = zip_open('huge_video.zip');
$entry = zip_read($zip);
zip_entry_open($zip, $entry);

header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="video.mp4"');

while ($chunk = zip_entry_read($entry, 65536)) {
    echo $chunk;
    flush(); // Сброс буфера вывода
    if (ob_get_level()) ob_flush();
}
zip_entry_close($entry);
zip_close($zip);
?>
Пример 4: Обработка вложенного архива в памяти

Чтение ZIP-файла, который находится внутри другого ZIP-архива, и обработка его содержимого без извлечения на диск.

Пример php
<?
// Открываем внешний архив
$outerZip = zip_open('nested.zip');
$outerEntry = zip_read($outerZip);
zip_entry_open($outerZip, $outerEntry);

// Читаем весь вложенный архив в строку
$innerZipData = '';
while ($data = zip_entry_read($outerEntry)) {
    $innerZipData .= $data;
}
zip_entry_close($outerEntry);
zip_close($outerZip);

// Сохраняем данные вложенного архива во временный файл
$tempFile = tempnam(sys_get_temp_dir(), 'inner');
file_put_contents($tempFile, $innerZipData);

// Открываем вложенный архив как новый
$innerZip = zip_open($tempFile);
// ... работа с содержимым $innerZip ...
zip_close($innerZip);
unlink($tempFile);
?>

PHP zip_entry_read function comments

En
Zip entry read Read from an open directory entry