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

PHP FTP 操作類完整實作:連線、上傳下載、移動複製、目錄遞歸建立與錯誤處理

     編輯:PHP 相關知識

PHP FTP 操作類完整實作:連線、上傳下載、移動複製、目錄遞歸建立與錯誤處理

PHP 內建 ftp_* 函數提供底層 FTP 協議支援,但直接使用較繁瑣。本文封裝一個 FtpClient 類,涵蓋連線管理、被動模式、檔案上傳/下載/移動/複製/刪除、目錄遞歸建立與錯誤回報,開箱即用。

核心類程式碼

<?php
/**
 * FTP 操作封裝類
 * 特性:自動被動模式、目錄遞歸建立、複製轉為下載+上傳、錯誤訊息收集
 */
class FtpClient {
    private $conn;
    public  $lastError = '';
    
    /** 建立連線並登入
     * @param string $host
     * @param int    $port
     * @param string $user
     * @param string $pass
     * @param int    $timeout 秒
     * @throws Exception 連線/登入失敗
     */
    public function __construct($host, $port = 21, $user, $pass, $timeout = 30) {
        $this->conn = @ftp_connect($host, $port, $timeout);
        if (!$this->conn) throw new Exception("FTP 連線失敗: $host:$port");
        if (!@ftp_login($this->conn, $user, $pass)) throw new Exception("FTP 登入失敗: $user");
        @ftp_pasv($this->conn, true); // 被動模式,適應防火牆/NAT
    }
    
    /** 關閉連線 */
    public function close() {
        if ($this->conn) @ftp_close($this->conn);
        $this->conn = null;
    }
    
    /** 解構子自動關閉 */
    public function __destruct() { $this->close(); }
    
    /** 取得最後錯誤 */
    public function getLastError() { return $this->lastError; }
    
    // ---------- 目錄操作 ----------
    
    /** 遞歸建立遠端目錄 (含多層)
     * @param string $remotePath 如 "data/2024/05/"
     * @return bool
     */
    private function mkDirRecursive($remotePath) {
        $dirs = explode('/', trim($remotePath, '/'));
        $current = '';
        foreach ($dirs as $dir) {
            if ($dir === '') continue;
            $current .= "/$dir";
            if (!@ftp_chdir($this->conn, $current)) {
                if (!@ftp_mkdir($this->conn, $current)) {
                    $this->lastError = "建立目錄失敗: $current";
                    return false;
                }
                @ftp_chdir($this->conn, $current);
            }
        }
        // 回到根目錄
        for ($i = 0; $i < count($dirs); $i++) @ftp_cdup($this->conn);
        return true;
    }
    
    // ---------- 檔案操作 ----------
    
    /** 上傳本地檔案
     * @param string $localPath  本地絕對路徑
     * @param string $remotePath 遠端相對路徑 (含檔名)
     * @param bool   $autoMkdir  自動建立目標目錄
     * @return bool
     */
    public function upload($localPath, $remotePath, $autoMkdir = true) {
        if (!is_file($localPath)) { $this->lastError = "本地檔案不存在: $localPath"; return false; }
        if ($autoMkdir) {
            $dir = dirname($remotePath);
            if ($dir !== '.' && !$this->mkDirRecursive($dir)) return false;
        }
        if (!@ftp_put($this->conn, $remotePath, $localPath, FTP_BINARY)) {
            $this->lastError = "上傳失敗: $localPath -> $remotePath";
            return false;
        }
        return true;
    }
    
    /** 下載遠端檔案
     * @param string $remotePath
     * @param string $localPath
     * @return bool
     */
    public function download($remotePath, $localPath) {
        $dir = dirname($localPath);
        if (!is_dir($dir) && !mkdir($dir, 0755, true)) { $this->lastError = "本地目錄建立失敗: $dir"; return false; }
        if (!@ftp_get($this->conn, $localPath, $remotePath, FTP_BINARY)) {
            $this->lastError = "下載失敗: $remotePath -> $localPath";
            return false;
        }
        return true;
    }
    
    /** 移動/重命名遠端檔案 (FTP RENAME)
     * @param string $oldPath
     * @param string $newPath
     * @param bool   $autoMkdir
     * @return bool
     */
    public function move($oldPath, $newPath, $autoMkdir = true) {
        if ($autoMkdir) {
            $dir = dirname($newPath);
            if ($dir !== '.' && !$this->mkDirRecursive($dir)) return false;
        }
        if (!@ftp_rename($this->conn, $oldPath, $newPath)) {
            $this->lastError = "移動失敗: $oldPath -> $newPath";
            return false;
        }
        return true;
    }
    
    /** 複製遠端檔案 (FTP 無 COPY,採下載+上傳)
     * @param string $srcPath
     * @param string $dstPath
     * @param bool   $autoMkdir
     * @return bool
     */
    public function copy($srcPath, $dstPath, $autoMkdir = true) {
        $tmp = sys_get_temp_dir() . '/ftp_copy_' . uniqid() . '.tmp';
        if (!$this->download($srcPath, $tmp)) return false;
        $ok = $this->upload($tmp, $dstPath, $autoMkdir);
        @unlink($tmp);
        return $ok;
    }
    
    /** 刪除遠端檔案 */
    public function delete($remotePath) {
        if (!@ftp_delete($this->conn, $remotePath)) {
            $this->lastError = "刪除失敗: $remotePath";
            return false;
        }
        return true;
    }
    
    /** 列出目錄檔案 (簡易 nlist) */
    public function listFiles($remotePath = '.') {
        return @ftp_nlist($this->conn, $remotePath) ?: [];
    }
    
    /** 取得檔案大小 (bytes) */
    public function fileSize($remotePath) {
        $size = @ftp_size($this->conn, $remotePath);
        return $size >= 0 ? $size : false; // -1 表目錄或不存在
    }
}
?>

使用範例

<?php
require_once 'FtpClient.php';

try {
    $ftp = new FtpClient('ftp.example.com', 21, 'user', 'pass');
    
    // 上傳
    $ftp->upload('/local/path/video.mp4', 'uploads/2024/05/video.mp4');
    
    // 複製
    $ftp->copy('uploads/2024/05/video.mp4', 'backup/video.mp4');
    
    // 移動
    $ftp->move('uploads/2024/05/video.mp4', 'archive/video.mp4');
    
    // 列表
    print_r($ftp->listFiles('uploads'));
    
    $ftp->close();
} catch (Exception $e) {
    echo "FTP 錯誤: " . $e->getMessage();
}
?>

進階補充:獨立建立目錄函數

若不需要完整類,僅需單一函數建立遠端目錄:

function ftpMkdirRecursive($host, $user, $pass, $remotePath, $port = 21) {
    $conn = ftp_connect($host, $port);
    if (!$conn || !ftp_login($conn, $user, $pass)) return false;
    ftp_pasv($conn, true);
    $dirs = explode('/', trim($remotePath, '/'));
    $cur = '';
    foreach ($dirs as $d) {
        $cur .= "/$d";
        if (!@ftp_chdir($conn, $cur) && !@ftp_mkdir($conn, $cur)) { ftp_close($conn); return false; }
    }
    ftp_close($conn);
    return true;
}

常見坑點提醒

  • 被動模式必開:ftp_pasv($conn, true),否則資料連線常被防火牆擋住導致掛起。
  • 二進制模式:FTP_BINARY 適用所有檔案;僅純文字可用 FTP_ASCII。
  • 路徑分隔符:FTP 伺服器通常用 /,即使 Windows 伺服器也建議統一用正斜線。
  • 超時處理:大檔傳輸調大 ftp_connect 第 3 參數或 ini_set('default_socket_timeout', 300)。
  • 錯誤抑制 @:範例使用 @ 抑制警告,改由回傳值與 $lastError 統一處理,生產環境建議改用 error_get_last() 記錄詳細資訊。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved