首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 文件压缩与目录递归删除实战:tar.gz 打包与安全清理

     編輯:PHP 綜合知識

系统级文件操作:PHP 调用 Shell 打包压缩与递归删除目录

PHP 原生 ZipArchive 处理大文件/深目录易超时、内存溢出。调用系统 tar 命令打包配合递归删除函数,是高性能文件清理的实用方案。本文提供生产可用的完整实现。

一、tar.gz 压缩打包

<?php
$dir = './temp';           // 待打包目录
$dest = 'backup_' . date('Ymd_His') . '.tar.gz'; // 输出文件

// 安全转义参数,防命令注入
$dirEsc = escapeshellarg($dir);
$destEsc = escapeshellarg($dest);

// 执行打包:-c 创建 -z gzip压缩 -v 显示过程 -f 文件名
$cmd = "tar czvf {$destEsc} {$dirEsc}";
$output = [];
$returnCode = 0;
exec($cmd, $output, $returnCode);

if ($returnCode === 0) {
    echo "✅ 打包成功: {$dest} (" . filesize($dest) . " bytes)\n";
    // 可选:打包后删除源目录
    // recursiveDelete($dir);
} else {
    echo "❌ 打包失败 (code: {$returnCode}):\n";
    print_r($output);
}

// 解压命令参考
// exec("tar zxvf " . escapeshellarg($dest) . " -C " . escapeshellarg('./restore'));
?>

二、递归删除目录及内容 (健壮版)

<?php
/**
 * 递归删除目录及所有内容
 * @param string $dirPath 目录路径
 * @param bool $deleteSelf 是否删除目录本身 (false 仅清空)
 * @return bool 成功返回 true
 */
function recursiveDelete(string $dirPath, bool $deleteSelf = true): bool {
    // 规范化路径
    $dirPath = rtrim($dirPath, '/\\');
    
    if (!is_dir($dirPath)) {
        trigger_error("目录不存在: $dirPath", E_USER_WARNING);
        return false;
    }
    
    // 权限修正:确保可读可写可执行
    @chmod($dirPath, 0755);
    
    $items = scandir($dirPath);
    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        
        $path = $dirPath . DIRECTORY_SEPARATOR . $item;
        
        // 处理符号链接:仅删除链接,不跟随
        if (is_link($path)) {
            @unlink($path);
            continue;
        }
        
        if (is_dir($path)) {
            // 递归删除子目录
            recursiveDelete($path, true);
        } else {
            // 删除文件:先尝试修改权限
            @chmod($path, 0644);
            @unlink($path);
        }
    }
    
    if ($deleteSelf) {
        return @rmdir($dirPath);
    }
    return true;
}

// 使用示例
if (recursiveDelete("./temp")) {
    echo "✅ 目录清理成功\n";
} else {
    echo "❌ 清理失败,请检查权限\n";
}
?>

三、生产级封装:FileCleaner 类

<?php
class FileCleaner {
    private $basePath;
    private $dryRun = false;
    private $errors = [];
    private $deleted = 0;
    private $freedBytes = 0;

    public function __construct(string $basePath) {
        $this->basePath = rtrim(realpath($basePath), '/\\');
    }

    public function setDryRun(bool $dryRun): self {
        $this->dryRun = $dryRun;
        return $this;
    }

    // 按模式删除 (glob 支持)
    public function deleteByPattern(string $pattern): int {
        $files = glob($this->basePath . DIRECTORY_SEPARATOR . $pattern);
        foreach ($files as $file) {
            $this->deletePath($file);
        }
        return $this->deleted;
    }

    // 按修改时间删除 (早于 N 天)
    public function deleteOlderThan(int $days): int {
        $cutoff = time() - $days * 86400;
        $this->scanAndDelete($this->basePath, function($path) use ($cutoff) {
            return filemtime($path) < $cutoff;
        });
        return $this->deleted;
    }

    // 按大小删除 (大于 N 字节)
    public function deleteLargerThan(int $bytes): int {
        $this->scanAndDelete($this->basePath, function($path) use ($bytes) {
            return filesize($path) > $bytes;
        });
        return $this->deleted;
    }

    // 清空目录 (保留目录)
    public function emptyDirectory(string $subPath = ''): bool {
        $target = $this->basePath . ($subPath ? DIRECTORY_SEPARATOR . $subPath : '');
        return $this->deletePath($target, false);
    }

    private function scanAndDelete(string $dir, callable $filter) {
        $items = @scandir($dir);
        if (!$items) return;
        
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') continue;
            $path = $dir . DIRECTORY_SEPARATOR . $item;
            
            if (is_dir($path) && !is_link($path)) {
                $this->scanAndDelete($path, $filter);
                // 子目录清空后尝试删除
                if ($filter($path)) $this->deletePath($path);
            } elseif ($filter($path)) {
                $this->deletePath($path);
            }
        }
    }

    private function deletePath(string $path, bool $deleteDir = true) {
        if (!file_exists($path) && !is_link($path)) return;
        
        $size = is_file($path) ? filesize($path) : 0;
        
        if ($this->dryRun) {
            echo "[DRY-RUN] Would delete: $path\n";
            $this->deleted++;
            $this->freedBytes += $size;
            return;
        }
        
        if (is_dir($path) && !is_link($path)) {
            if ($deleteDir) {
                $success = recursiveDelete($path);
            } else {
                $success = recursiveDelete($path, false);
            }
        } else {
            @chmod($path, 0644);
            $success = @unlink($path);
        }
        
        if ($success) {
            $this->deleted++;
            $this->freedBytes += $size;
        } else {
            $this->errors[] = "删除失败: $path";
        }
    }

    public function getReport(): array {
        return [
            'deleted_count' => $this->deleted,
            'freed_bytes' => $this->freedBytes,
            'freed_human' => $this->formatBytes($this->freedBytes),
            'errors' => $this->errors,
        ];
    }

    private function formatBytes(int $bytes): string {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $i = 0;
        while ($bytes >= 1024 && $i < count($units) - 1) {
            $bytes /= 1024;
            $i++;
        }
        return round($bytes, 2) . ' ' . $units[$i];
    }
}

// 使用示例
$cleaner = new FileCleaner('/var/www/storage/temp')
    ->setDryRun(false); // 先 true 测试,确认后改 false

$cleaner->deleteOlderThan(7);      // 删除 7 天前文件
$cleaner->deleteByPattern('*.log'); // 删除所有 .log
$cleaner->deleteLargerThan(100 * 1024 * 1024); // 删除 >100MB 文件

$report = $cleaner->getReport();
echo "清理报告: 删除了 {$report['deleted_count']} 项,释放 {$report['freed_human']}\n";
if ($report['errors']) print_r($report['errors']);
?>

四、安全注意事项

  1. 命令注入防护:所有传入 Shell 的参数必须用 escapeshellarg()
  2. 路径遍历防护:使用 realpath() 规范化,确保在允许根目录下
  3. 符号链接处理:is_link() 识别,仅删除链接不跟随
  4. 权限最小化:PHP 进程用户仅拥有目标目录读写权限
  5. 干运行模式:生产首次执行务必 dryRun=true 预览

五、定时任务部署 (Cron)

# 每天凌晨 3 点清理 7 天前临时文件
0 3 * * * /usr/bin/php /opt/scripts/clean_temp.php >> /var/log/clean_temp.log 2>&1

# clean_temp.php 内容
<?php
require 'FileCleaner.php';
$cleaner = new FileCleaner('/var/www/temp')->setDryRun(false);
$cleaner->deleteOlderThan(7);
$report = $cleaner->getReport();
// 可选:邮件/钉钉推送报告
?>
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved