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

PHP 實現多檔案複製與移動的工具類

     編輯:PHP 綜合知識

參考 elFinder 架構,封裝一個輕量級的檔案操作工具,支援多檔案批量複製/移動、目錄遞迴複製、存在性檢查、自我保護 (防止移動到自身子目錄) 與日誌記錄。適合整合至後台管理系統的檔案管理模組。

核心類別結構

class FileOperator {
    private $baseDir;   // 專案根目錄 (預設為腳本所在目錄)
    private $logFile;   // 日誌檔案路徑

    public function __construct($baseDir = null, $logFile = 'file_operator.log') {
        $this->baseDir = $baseDir ?: dirname(__FILE__);
        $this->logFile = $this->baseDir . DIRECTORY_SEPARATOR . $logFile;
    }

    /** 公開介面:批量複製或移動
     * @param array  $sources 來源檔案/目錄相對路徑陣列
     * @param string $destDir 目的目錄相對路徑
     * @param string $op      'copy' 或 'move'
     * @return array ['success' => bool, 'message' => string, 'details' => array]
     */
    public function copyMove(array $sources, $destDir, $op = 'move') {
        $destAbs = $this->resolvePath($destDir);
        if (!is_dir($destAbs)) {
            if (!mkdir($destAbs, 0755, true)) {
                return $this->fail("無法建立目的目錄: $destDir");
            }
        }

        $results = [];
        foreach ($sources as $srcRel) {
            $srcAbs = $this->resolvePath($srcRel);
            if (!file_exists($srcAbs)) {
                $results[$srcRel] = '來源不存在';
                continue;
            }
            // 防止移動到自身或子目錄內
            if ($op === 'move' && strpos($destAbs, $srcAbs) === 0) {
                $results[$srcRel] = '不可移動到自身或子目錄';
                continue;
            }
            $targetAbs = $destAbs . DIRECTORY_SEPARATOR . basename($srcAbs);
            if (file_exists($targetAbs)) {
                $results[$srcRel] = '目標已存在同名檔案/目錄';
                continue;
            }

            $ok = ($op === 'move')
                ? @rename($srcAbs, $targetAbs)
                : $this->deepCopy($srcAbs, $targetAbs);

            $results[$srcRel] = $ok ? '成功' : '操作失敗 (權限或磁碟空間不足?)';
        }
        $this->log("Batch $op", $results);
        return ['success' => true, 'message' => '批次處理完成', 'details' => $results];
    }

    /** 遞迴深度複製 */
    private function deepCopy($src, $dst) {
        if (is_file($src)) {
            return @copy($src, $dst);
        }
        if (!@mkdir($dst, 0755, true)) return false;
        $items = scandir($src);
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') continue;
            if (!$this->deepCopy("$src/$item", "$dst/$item")) return false;
        }
        return true;
    }

    /** 解析相對路徑為絕對路徑,防止目錄遍歷攻擊 */
    private function resolvePath($rel) {
        $abs = realpath($this->baseDir . DIRECTORY_SEPARATOR . $rel);
        if ($abs === false || strpos($abs, realpath($this->baseDir)) !== 0) {
            throw new InvalidArgumentException("非法路徑: $rel");
        }
        return $abs;
    }

    private function log($action, $details) {
        $msg = date('Y-m-d H:i:s') . " [$action] " . json_encode($details, JSON_UNESCAPED_UNICODE) . PHP_EOL;
        file_put_contents($this->logFile, $msg, FILE_APPEND | LOCK_EX);
    }

    private function fail($msg) {
        $this->log('ERROR', $msg);
        return ['success' => false, 'message' => $msg, 'details' => []];
    }
}

使用範例

$operator = new FileOperator('/var/www/project');

// 複製多個檔案/目錄到 backup 資料夾
$src = ['public/uploads/avatar.jpg', 'storage/logs', 'config/app.php'];
$res = $operator->copyMove($src, 'backup/2024-01-15', 'copy');
print_r($res);

// 移動單一檔案
$res = $operator->copyMove(['tmp/old_report.pdf'], 'archive/2023', 'move');
print_r($res);

設計重點

  • 路徑安全:resolvePath() 使用 realpath() 確保操作限制在專案根目錄下,防止 ../../etc/passwd 類攻擊。
  • 原子性與回滾:目前為逐個處理,失敗不回滾前面成功項。關鍵業務建議加入事務機制 (記錄操作清單,失敗時反向執行)。
  • 效能考量:大目錄複製建議改用 symlink 或系統指令 cp -r / rsync (透過 exec()),速度快數量級。
  • 擴充性:易於加入覆蓋模式、重命名衝突策略、進度回呼、壓縮打包等功能。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved