Исправлен битый архив бэкапа - использование временного файла

shell_exec некорректно работает с бинарными данными.
Теперь mysqldump пишет во временный файл, который потом стримится клиенту.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-23 22:24:11 +03:00
parent cf275bba9c
commit 978f6b3cd8
2 changed files with 46 additions and 20 deletions

View File

@@ -23,30 +23,36 @@ class BackupService
}
/**
* Создать дамп и вернуть содержимое (gzip сжатое)
* Создать дамп и вернуть путь к временному файлу
*/
public function dumpDatabase(string $database): string
{
$tempFile = sys_get_temp_dir() . '/backup_' . $database . '_' . uniqid() . '.sql.gz';
$cmd = sprintf(
'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers %s 2>/dev/null | gzip',
'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers %s 2>&1 | gzip > %s',
escapeshellarg($this->host),
escapeshellarg($this->port),
escapeshellarg($this->user),
escapeshellarg($this->pass),
escapeshellarg($database)
escapeshellarg($database),
escapeshellarg($tempFile)
);
$output = shell_exec($cmd);
exec($cmd, $output, $returnCode);
if ($output === null || $output === '') {
throw new \RuntimeException("Failed to create dump for database: $database");
if ($returnCode !== 0 || !file_exists($tempFile) || filesize($tempFile) < 100) {
if (file_exists($tempFile)) {
unlink($tempFile);
}
throw new \RuntimeException("mysqldump failed: " . implode("\n", $output));
}
return $output;
return $tempFile;
}
/**
* Создать дамп всех баз данных (один файл)
* Создать дамп всех баз данных
*/
public function dumpAllDatabases(\PDO $pdo): string
{
@@ -56,23 +62,28 @@ class BackupService
throw new \RuntimeException("No databases available for backup");
}
$tempFile = sys_get_temp_dir() . '/backup_all_' . uniqid() . '.sql.gz';
$dbList = implode(' ', array_map('escapeshellarg', $databases));
$cmd = sprintf(
'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers --databases %s 2>/dev/null | gzip',
'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers --databases %s 2>&1 | gzip > %s',
escapeshellarg($this->host),
escapeshellarg($this->port),
escapeshellarg($this->user),
escapeshellarg($this->pass),
$dbList
$dbList,
escapeshellarg($tempFile)
);
$output = shell_exec($cmd);
exec($cmd, $output, $returnCode);
if ($output === null || $output === '') {
throw new \RuntimeException("Failed to create dump");
if ($returnCode !== 0 || !file_exists($tempFile) || filesize($tempFile) < 100) {
if (file_exists($tempFile)) {
unlink($tempFile);
}
throw new \RuntimeException("mysqldump failed: " . implode("\n", $output));
}
return $output;
return $tempFile;
}
}