非阻塞下載(支援大檔案/進度條)
function ftp_nb_download(string $host, string $user, string $pass, string $remote, string $local, callable $progress = null): bool {
$conn = ftp_connect($host) or die("連接失敗");
ftp_login($conn, $user, $pass) or die("登入失敗");
ftp_pasv($conn, true);
$localHandle = fopen($local, 'w') or die("無法建立本地檔案");
$ret = ftp_nb_fget($conn, $localHandle, $remote, FTP_BINARY, 0);
while ($ret === FTP_MOREDATA) {
if ($progress) {
$progress(ftell($localHandle), ftp_size($conn, $remote));
}
$ret = ftp_nb_continue($conn);
}
fclose($localHandle);
ftp_close($conn);
if ($ret !== FTP_FINISHED) {
unlink($local);
return false;
}
return true;
}
// 使用:顯示進度
ftp_nb_download('ftp.example.com', 'u', 'p', 'big.iso', 'big.iso', function($downloaded, $total) {
$pct = $total ? round($downloaded/$total*100, 1) : '?';
echo "\r已下載: $downloaded / $total ($pct%)";
});
刪除遠端檔案
function ftp_delete_file(string $host, string $user, string $pass, string $remote): bool {
$conn = ftp_connect($host) or return false;
ftp_login($conn, $user, $pass) or return false;
$ok = @ftp_delete($conn, $remote);
ftp_close($conn);
return $ok;
}
獲取遠端檔案大小
function ftp_remote_size(string $host, string $user, string $pass, string $remote): ?int {
$conn = ftp_connect($host) or return null;
ftp_login($conn, $user, $pass) or return null;
$size = ftp_size($conn, $remote); // -1 表示失敗或非檔案
ftp_close($conn);
return $size >= 0 ? $size : null;
}
批次操作封裝類
class FtpClient {
private $conn;
public function __construct(string $host, string $user, string $pass, int $port = 21, bool $pasv = true) {
$this->conn = ftp_connect($host, $port, 30);
if (!$this->conn || !ftp_login($this->conn, $user, $pass)) {
throw new Exception("FTP 連接/登入失敗");
}
ftp_pasv($this->conn, $pasv);
}
public function download(string $remote, string $local, callable $progress = null): bool { /* ... */ }
public function upload(string $local, string $remote): bool { /* ftp_put / ftp_nb_fput */ }
public function delete(string $remote): bool { return @ftp_delete($this->conn, $remote); }
public function size(string $remote): ?int { $s = ftp_size($this->conn, $remote); return $s>=0?$s:null; }
public function list(string $dir = '.'): array { return ftp_nlist($this->conn, $dir); }
public function __destruct() { if ($this->conn) ftp_close($this->conn); }
}
安全建議
- 生產環境改用 SFTP (SSH2) 或 FTPS,避免明文傳輸密碼與數據。
- PHP 可用
phpseclib/Net_SFTP 純 PHP 實現,無需 ssh2 擴展。
- 限制 FTP 用戶權限:
chroot 至特定目錄,唯讀/寫入分離帳號。