Инструменты PHP для восстановления файлов из архивов и директорий
Основные подходы к восстановлению файлов
Эффективное восстановление из ZIP-архива
Как восстановить все файлы проекта из zip-архива, созданного резервной копией?
Этот метод использует встроенное расширение ZipArchive. Скрипт открывает архив, извлекает его содержимое в целевую директорию и закрывает архив. Перед использованием необходимо убедиться в наличии расширения (extension=zip) и прав на запись.
$zip = new ZipArchive();
if ($zip->open('/backup/site_backup.zip') === TRUE) {
$zip->extractTo('/var/www/html/');
$zip->close();
echo 'Восстановление завершено.';
} else {
echo 'Ошибка открытия архива.';
}файл restore php (восстановление файлов в php)
Пояснение шагов: Параметр open принимает путь к архиву. extractTo указывает целевую папку. Если папка не существует, она не создается автоматически. После успешного извлечения архив закрывается. Для больших архивов используйте set_time_limit(0).
Типичные ошибки: расширение ZipArchive не загружено (проверьте php.ini); недостаточно прав на запись в целевую директорию; путь к архиву неверен; при большом количестве мелких файлов возможен переполнение памяти. Решение: установка временного лимита и использование статичных методов извлечения с callable-обработчиком для пофайлового контроля.
Цель метода - быстрое полное восстановление содержимого архива. Случаи использования: восстановление после сбоя сервера, разворачивание проекта на новом хостинге, перенос данных между серверами.
Как восстановить файлы из простой копии директории?
Если резервная копия представляет собой набор файлов и папок без архивации, применяют рекурсивное копирование. Данный подход прост, но неэффективен для глубоких деревьев.
function copyRecursive($src, $dst) {
$dir = opendir($src);
if (!is_dir($dst)) {
mkdir($dst, 0755, true);
}
while (($file = readdir($dir)) !== false) {
if ($file != '.' && $file != '..') {
$srcFile = "$src/$file";
$dstFile = "$dst/$file";
if (is_dir($srcFile)) {
copyRecursive($srcFile, $dstFile);
} else {
copy($srcFile, $dstFile);
}
}
}
closedir($dir);
}
copyRecursive('/backup/files', '/var/www/html');Здесь вручную открывается директория, и для каждого элемента создается целевая папка или копируется файл. Альтернатива - использование RecursiveDirectoryIterator.
Проблемы: при глубокой вложенности возможен переполнение стека рекурсии; не переносятся права доступа и владельцы; большая нагрузка на диск при множестве файлов. Решение: увеличение лимита вложенности с помощью xdebug.max_nesting_level или переход на итераторы.
Цель - быстрое восстановление структуры без установки дополнительных расширений. Случаи использования: мелкие проекты, тестовые среды, миграция между локальными папками.
Как восстановить файлы через FTP при отсутствии прямого доступа к файловой системе?
Если сервер не имеет прямого доступа к хранилищу резервных копий (например, внешний хост), используется FTP-соединение. PHP предоставляет набор ftp-функций для работы с удаленной файловой системой.
$ftp = ftp_connect('backup.example.com');
ftp_login($ftp, 'user', 'pass');
ftp_pasv($ftp, true);
$localPath = '/var/www/html';
$remotePath = '/backup/files';
// рекурсивная загрузка всех файлов
$files = ftp_nlist($ftp, $remotePath);
if ($files) {
foreach ($files as $remoteFile) {
$localFile = $localPath . '/' . basename($remoteFile);
ftp_get($ftp, $localFile, $remoteFile, FTP_BINARY);
}
}
ftp_close($ftp);Пояснение: сначала устанавливается пассивный режим (FTP_PASV). С помощью ftp_nlist получаем список файлов, затем загружаем каждый. Для рекурсивного обхода папок требуется собственная рекурсивная функция.
Типичные ошибки: блокировка портов на сервере (21, 20); необходимость SSL (ftps://); падение соединения при большом количестве файлов; разные права доступа. Рекомендуется использовать ftp_ssl_connect при подключении по FTPS и таймаут на соединение.
Цель - восстановление данных с удаленного хранилища без монтирования сетевой папки. Случаи использования: облачные бэкапы, внешние FTP-серверы, хостинг с ограниченным доступом к shell.
Как восстановить файлы из tar-архивов с помощью Phar?
Для поддержки tar, tar.gz, tar.bz2 используется класс PharData (расширение Phar). Метод extractTo извлекает весь архив в указанную папку.
$phar = new PharData('/backup/site.tar.gz');
$phar->extractTo('/var/www/html/', null, true);
echo 'Файлы извлечены из tar.gz';Третий параметр (overwrite) - перезаписывать ли существующие файлы. Если требуется извлечь только отдельные файлы, вторым параметром передается массив путей.
Проблемы: расширение Phar может быть отключено в php.ini; нет автоматического создания целевой директории; при извлечении большого количества файлов возможен рост памяти. Решение: предварительная проверка существования папки, использование phar.readonly = Off в php.ini.
Цель - работа с распространенными форматами архивации без внешних утилит. Случаи использования: Linux-серверы, где tar является стандартом; бэкапы, снятые через cron.
Как восстановить файлы через shell rsync для синхронизации целых папок?
Если на сервере разрешены функции exec или shell_exec, можно выполнить системную команду rsync. Это эффективно для больших объемов данных.
$backupDir = '/backup/site/'; // источник (локальный)
$targetDir = '/var/www/html/'; // назначение
$command = "rsync -av --delete $backupDir $targetDir";
$output = shell_exec($command);
echo "$output
";Флаг -av - архивирование и подробный вывод, --delete - удаление файлов в целевой папке, которых нет в источнике.
Ошибки: rsync может быть не установлен; недостаточно прав для выполнения shell-команд в php.ini (disable_functions); опасность внедрения команд. Всегда экранируйте пути с помощью escapeshellarg.
Цель - синхронизация целых деревьев каталогов с минимальным трафиком. Случаи использования: восстановление на локальной машине, зеркалирование, инкрементальные бэкапы.
Как восстановить отдельные файлы из базы данных (BLOB-хранение)?
Иногда файлы сохраняются в таблицах MySQL в виде BLOB. Тогда восстановление происходит через SQL-запросы с последующей записью на диск.
$pdo = new PDO('mysql:host=localhost;dbname=backup', 'user', 'pass');
$stmt = $pdo->query('SELECT filename, filedata FROM file_backup WHERE id = 1');
$row = $stmt->fetch(PDO::FETCH_ASSOC);
file_put_contents('/var/www/html/' . $row['filename'], $row['filedata']);Обратите внимание: BLOB может быть очень большим, поэтому используйте PDO::PARAM_LOB и потоковую запись.
Проблемы: переполнение памяти при чтении большого BLOB; время выполнения запроса; кодировка, если файл текстовый. Решение: загрузка частями через stream.
Цель - восстановление единичных файлов, если резервное копирование велось в СУБД. Случаи использования: CMS с хранением изображений в базе, мелкие проекты.
Проверка целостности восстановленных файлов по контрольным суммам
После восстановления полезно сверить хеши файлов с эталонными, чтобы убедиться в отсутствии ошибок.
$hashesFile = '/backup/hashes.json';
if (!file_exists($hashesFile)) {
echo 'Файл с хешами не найден.';
exit;
}
$hashes = json_decode(file_get_contents($hashesFile), true);
$errors = [];
foreach ($hashes as $relativePath => $expectedHash) {
$fullPath = '/var/www/html/' . ltrim($relativePath, '/');
if (file_exists($fullPath)) {
$actualHash = md5_file($fullPath);
if ($actualHash !== $expectedHash) {
$errors[] = "Не совпадает хеш для $relativePath (ожидалось $expectedHash, получено $actualHash)";
}
} else {
$errors[] = "Файл $relativePath отсутствует";
}
}
if (empty($errors)) {
echo 'Все файлы прошли проверку целостности.';
} else {
echo 'Обнаружены ошибки:
' . implode('
', $errors);
}Все файлы прошли проверку целостности.
Восстановление с логом операций и прерыванием при ошибке
Для крупных восстановлений полезно вести лог и останавливаться при первой ошибке, чтобы не копить неверные данные.
$logFile = '/var/log/restore.log';
$logHandle = fopen($logFile, 'a');
$fail = false;
$zip = new ZipArchive();
if ($zip->open('/backup/site.zip') === TRUE) {
for ($i = 0; $i < $zip->numFiles; $i++) {
$filename = $zip->getNameIndex($i);
if ($zip->extractTo('/var/www/html/', $filename) === false) {
fwrite($logHandle, "[ERROR] Не удалось извлечь $filename\n");
$fail = true;
break; // прерываем
}
fwrite($logHandle, "[OK] $filename\n");
}
$zip->close();
} else {
fwrite($logHandle, "[FATAL] Не удалось открыть архив\n");
$fail = true;
}
fclose($logHandle);
if ($fail) {
echo 'Восстановление прервано из-за ошибок. Подробности в логе.';
} else {
echo 'Восстановление успешно, лог сохранен.';
}Восстановление прервано из-за ошибок. Подробности в логе.
Восстановление с переименованием конфликтующих файлов (добавление временной метки)
Если требуется сохранить существующие файлы, а не перезаписывать их, можно добавить к имени метку времени.
$zip = new ZipArchive();
$targetDir = '/var/www/html/';
if ($zip->open('/backup/site.zip') === TRUE) {
for ($i = 0; $i < $zip->numFiles; $i++) {
$filename = $zip->getNameIndex($i);
$targetPath = $targetDir . $filename;
if (file_exists($targetPath)) {
$info = pathinfo($targetPath);
$newName = $info['filename'] . '_' . date('YmdHis') . '.' . ($info['extension'] ?? '');
$targetPath = $info['dirname'] . '/' . $newName;
}
$zip->extractTo($targetDir, $filename);
if ($targetPath !== $targetDir . $filename) {
rename($targetDir . $filename, $targetPath);
}
}
$zip->close();
echo 'Файлы восстановлены, конфликты переименованы.';
} else {
echo 'Ошибка открытия архива.';
}Файлы восстановлены, конфликты переименованы.
Потоковая загрузка больших файлов из FTP с возобновлением
Для файлов размером более 2 ГБ или при нестабильном соединении используется ftp_nb_get (non-blocking).
$ftp = ftp_connect('backup.example.com');
ftp_login($ftp, 'user', 'pass');
ftp_pasv($ftp, true);
$remoteFile = '/backups/large.sql.gz';
$localFile = '/var/www/html/large.sql.gz';
$ret = ftp_nb_get($ftp, $localFile, $remoteFile, FTP_BINARY);
while ($ret == FTP_MOREDATA) {
echo '.'; // индикация прогресса
$ret = ftp_nb_continue($ftp);
}
if ($ret == FTP_FINISHED) {
echo "\nСкачивание завершено.";
} else {
echo "\nОшибка скачивания.";
}
ftp_close($ftp);.......... Скачивание завершено.
Восстановление с использованием SCP через proc_open
SCP - безопасный способ передачи файлов по SSH. В PHP можно запустить scp через proc_open.
$descriptorspec = [
0 => ['pipe', 'r'], // stdin
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'] // stderr
];
$process = proc_open(
'scp -r user@backup.server:/backup/site/* /var/www/html/',
$descriptorspec,
$pipes
);
if (is_resource($process)) {
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
$error = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$return_value = proc_close($process);
if ($return_value === 0) {
echo 'Восстановление через SCP выполнено.';
} else {
echo 'Ошибка SCP: ' . $error;
}
}Восстановление через SCP выполнено.