FTP是一種檔案傳輸通訊協定,它支援兩種模式,一種方式叫做Standard (也就是Active,主動方式),一種是 Passive (也就是PASV,被動方式)。 Standard模式 FTP 的用戶端發送 PORT 命令到FTP server。Passive模式FTP的用戶端發送 PASV命令到 FTP Server。
下面介紹一個這兩種方式的工作原理:
Standard模式
FTP 用戶端首先和FTP Server的TCP 21連接埠建立串連,通過這個通道 發送命令,用戶端需要接收資料的時候在這個通道上發送PORT命令。 PORT命令包含 了用戶端用什麼連接埠接收資料。在傳送資料的時候,伺服器端通過自己的TCP 20連接埠發送資料。 FTP server必須和用戶端建立一個新的串連用來傳送資料。
Passive模式
在建立控制通道的時候和Standard模式類似,當用戶端通過這個通道發送PASV 命令的時候,FTP server開啟一個位於1024和5000之間的隨機連接埠並且通知 用戶端在這個連接埠上傳送資料的請求,然後FTP server 將通過這個連接埠進行資料的傳送,這個時候FTP server不再需要建立一個新的和用戶端之間的串連。
使用PHP操作FTP-用法
複製代碼 代碼如下:
<?
// 聯結FTP伺服器
$conn = ftp_connect(ftp.server.com);
// 使用username和password登入
ftp_login($conn, “john”, “doe”);
// 擷取遠端系統類別型
ftp_systype($conn);
// 列示檔案
$filelist = ftp_nlist($conn, “.”);
// 下載檔案
ftp_get($conn, “data.zip”, “data.zip”, FTP_BINARY);
// 關閉聯結
ftp_quit($conn);
//初結化一個FTP聯結,PHP提供了ftp_connect()這個函數,它使用主機名稱和連接埠作為參數。在上面的例子裡,主機名稱字為 “ftp.server.com”;如果連接埠沒指定,PHP將會使用“21”作為預設連接埠來建立聯結。
//聯結成功後ftp_connect()傳回一個handle控制代碼;這個handle將被以後使用的FTP函數使用。
$conn = ftp_connect(ftp.server.com);
//一旦建立聯結,使用ftp_login()發送一個使用者名稱稱和使用者密碼。你可以看到,這個函數ftp_login()使用了 ftp_connect()函數傳來的handle,以確定使用者名稱和密碼能被提交到正確的伺服器。
ftp_login($conn, “john”, “doe”);
// close connection
ftp_quit($conn);
//登入了FTP伺服器,PHP提供了一些函數,它們能擷取一些關於系統和檔案以及目錄的資訊。
ftp_pwd()
//擷取當前所在的目錄
$here = ftp_pwd($conn);
//擷取伺服器端系統資訊ftp_systype()
$server_os = ftp_systype($conn);
//被動模式(PASV)的開關,開啟或關閉PASV(1表示開)
ftp_pasv($conn, 1);
//進入目錄中用ftp_chdir()函數,它接受一個目錄名作為參數。
ftp_chdir($conn, “public_html”);
//回到所在的目錄父目錄用ftp_cdup()實現
ftp_cdup($conn);
//建立或移動一個目錄,這要使用ftp_mkdir()和ftp_rmdir()函數;注意:ftp_mkdir()建立成功的話,就會返回建立立的目錄名。
ftp_mkdir($conn, “test”);
ftp_rmdir($conn, “test”);
//上傳檔案,ftp_put()函數能很好的勝任,它需要你指定一個本地檔案名稱,上傳後的檔案名稱以及傳輸的類型。比方說:如果你想上傳 “abc.txt”這個檔案,上傳後命名為“xyz.txt”,命令應該是這樣:
ftp_put($conn, “xyz.txt”, “abc.txt”, FTP_ASCII);
//下載檔案:PHP所提供的函數是ftp_get(),它也需要一個伺服器上檔案名稱,下載後的檔案名稱,以及傳輸類型作為參數,例如:伺服器端檔案為his.zip,你想下載至本地機,並命名為hers.zip,命令如下:
ftp_get($conn, “hers.zip”, “his.zip”, FTP_BINARY);
//PHP提供兩種方法:一種是簡單列示檔案名稱和目錄,另一種就是詳細的列示檔案的大小,許可權,創立時間等資訊。
//第一種使用ftp_nlist()函數,第二種用ftp_rawlist().兩種函數都需要一個目錄名做為參數,都返回目錄列做為一個數組,數組的每一個元素相當於列表的一行。
$filelist = ftp_nlist($conn, “.”);
//函數ftp_size(),它返回你所指定的檔案的大小,使用BITES作為單位。要指出的是,如果它返回的是 “-1”的話,意味著這是一個目錄
$filelist = ftp_size($conn, “data.zip”);
?>
FTP類
複製代碼 代碼如下:
<?php
/**
* 仿寫CodeIgniter的FTP類
* FTP基本操作:
* 1) 登陸; connect
* 2) 目前的目錄檔案清單; filelist
* 3) 目錄改變; chgdir
* 4) 重新命名/移動; rename
* 5) 建立檔案夾; mkdir
* 6) 刪除; delete_dir/delete_file
* 7) 上傳; upload
* 8) 下載 download
*
* @author quanshuidingdang
*/
class Ftp {
private $hostname = '';
private $username = '';
private $password = '';
private $port = 21;
private $passive = TRUE;
private $debug = TRUE;
private $conn_id = FALSE;
/**
* 建構函式
*
* @param array 配置數組 : $config = array('hostname'=>'','username'=>'','password'=>'','port'=>''...);
*/
public function __construct($config = array()) {
if(count($config) > 0) {
$this->_init($config);
}
}
/**
* FTP串連
*
* @access public
* @param array 配置數組
* @return boolean
*/
public function connect($config = array()) {
if(count($config) > 0) {
$this->_init($config);
}
if(FALSE === ($this->conn_id = @ftp_connect($this->hostname,$this->port))) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_connect");
}
return FALSE;
}
if( ! $this->_login()) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_login");
}
return FALSE;
}
if($this->passive === TRUE) {
ftp_pasv($this->conn_id, TRUE);
}
return TRUE;
}
/**
* 目錄改變
*
* @access public
* @param string 目錄標識(ftp)
* @param boolean
* @return boolean
*/
public function chgdir($path = '', $supress_debug = FALSE) {
if($path == '' OR ! $this->_isconn()) {
return FALSE;
}
$result = @ftp_chdir($this->conn_id, $path);
if($result === FALSE) {
if($this->debug === TRUE AND $supress_debug == FALSE) {
$this->_error("ftp_unable_to_chgdir:dir[".$path."]");
}
return FALSE;
}
return TRUE;
}
/**
* 目錄產生
*
* @access public
* @param string 目錄標識(ftp)
* @param int 檔案許可權列表
* @return boolean
*/
public function mkdir($path = '', $permissions = NULL) {
if($path == '' OR ! $this->_isconn()) {
return FALSE;
}
$result = @ftp_mkdir($this->conn_id, $path);
if($result === FALSE) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_mkdir:dir[".$path."]");
}
return FALSE;
}
if( ! is_null($permissions)) {
$this->chmod($path,(int)$permissions);
}
return TRUE;
}
/**
* 上傳
*
* @access public
* @param string 本地目錄標識
* @param string 遠程目錄標識(ftp)
* @param string 上傳模式 auto || ascii
* @param int 上傳後的檔案許可權列表
* @return boolean
*/
public function upload($localpath, $remotepath, $mode = 'auto', $permissions = NULL) {
if( ! $this->_isconn()) {
return FALSE;
}
if( ! file_exists($localpath)) {
if($this->debug === TRUE) {
$this->_error("ftp_no_source_file:".$localpath);
}
return FALSE;
}
if($mode == 'auto') {
$ext = $this->_getext($localpath);
$mode = $this->_settype($ext);
}
$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
$result = @ftp_put($this->conn_id, $remotepath, $localpath, $mode);
if($result === FALSE) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_upload:localpath[".$localpath."]/remotepath[".$remotepath."]");
}
return FALSE;
}
if( ! is_null($permissions)) {
$this->chmod($remotepath,(int)$permissions);
}
return TRUE;
}
/**
* 下載
*
* @access public
* @param string 遠程目錄標識(ftp)
* @param string 本地目錄標識
* @param string 下載模式 auto || ascii
* @return boolean
*/
public function download($remotepath, $localpath, $mode = 'auto') {
if( ! $this->_isconn()) {
return FALSE;
}
if($mode == 'auto') {
$ext = $this->_getext($remotepath);
$mode = $this->_settype($ext);
}
$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
$result = @ftp_get($this->conn_id, $localpath, $remotepath, $mode);
if($result === FALSE) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_download:localpath[".$localpath."]-remotepath[".$remotepath."]");
}
return FALSE;
}
return TRUE;
}
/**
* 重新命名/移動
*
* @access public
* @param string 遠程目錄標識(ftp)
* @param string 新目錄標識
* @param boolean 判斷是重新命名(FALSE)還是移動(TRUE)
* @return boolean
*/
public function rename($oldname, $newname, $move = FALSE) {
if( ! $this->_isconn()) {
return FALSE;
}
$result = @ftp_rename($this->conn_id, $oldname, $newname);
if($result === FALSE) {
if($this->debug === TRUE) {
$msg = ($move == FALSE) ? "ftp_unable_to_rename" : "ftp_unable_to_move";
$this->_error($msg);
}
return FALSE;
}
return TRUE;
}
/**
* 刪除檔案
*
* @access public
* @param string 檔案標識(ftp)
* @return boolean
*/
public function delete_file($file) {
if( ! $this->_isconn()) {
return FALSE;
}
$result = @ftp_delete($this->conn_id, $file);
if($result === FALSE) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_delete_file:file[".$file."]");
}
return FALSE;
}
return TRUE;
}
/**
* 刪除檔案夾
*
* @access public
* @param string 目錄標識(ftp)
* @return boolean
*/
public function delete_dir($path) {
if( ! $this->_isconn()) {
return FALSE;
}
//對目錄宏的'/'字元添加反斜線'\'
$path = preg_replace("/(.+?)\/*$/", "\\1/", $path);
//擷取目錄檔案清單
$filelist = $this->filelist($path);
if($filelist !== FALSE AND count($filelist) > 0) {
foreach($filelist as $item) {
//如果我們無法刪除,那麼就可能是一個檔案夾
//所以我們遞迴調用delete_dir()
if( ! @delete_file($item)) {
$this->delete_dir($item);
}
}
}
//刪除檔案夾(空檔案夾)
$result = @ftp_rmdir($this->conn_id, $path);
if($result === FALSE) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_delete_dir:dir[".$path."]");
}
return FALSE;
}
return TRUE;
}
/**
* 修改檔案許可權
*
* @access public
* @param string 目錄標識(ftp)
* @return boolean
*/
public function chmod($path, $perm) {
if( ! $this->_isconn()) {
return FALSE;
}
//只有在PHP5中才定義了修改許可權的函數(ftp)
if( ! function_exists('ftp_chmod')) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_chmod(function)");
}
return FALSE;
}
$result = @ftp_chmod($this->conn_id, $perm, $path);
if($result === FALSE) {
if($this->debug === TRUE) {
$this->_error("ftp_unable_to_chmod:path[".$path."]-chmod[".$perm."]");
}
return FALSE;
}
return TRUE;
}
/**
* 擷取目錄檔案清單
*
* @access public
* @param string 目錄標識(ftp)
* @return array
*/
public function filelist($path = '.') {
if( ! $this->_isconn()) {
return FALSE;
}
return ftp_nlist($this->conn_id, $path);
}
/**
* 關閉FTP
*
* @access public
* @return boolean
*/
public function close() {
if( ! $this->_isconn()) {
return FALSE;
}
return @ftp_close($this->conn_id);
}
/**
* FTP成員變數初始化
*
* @access private
* @param array 配置數組
* @return void
*/
private function _init($config = array()) {
foreach($config as $key => $val) {
if(isset($this->$key)) {
$this->$key = $val;
}
}
//特殊字元過濾
$this->hostname = preg_replace('|.+?://|','',$this->hostname);
}
/**
* FTP登陸
*
* @access private
* @return boolean
*/
private function _login() {
return @ftp_login($this->conn_id, $this->username, $this->password);
}
/**
* 判斷con_id
*
* @access private
* @return boolean
*/
private function _isconn() {
if( ! is_resource($this->conn_id)) {
if($this->debug === TRUE) {
$this->_error("ftp_no_connection");
}
return FALSE;
}
return TRUE;
}
/**
* 從檔案名稱中擷取尾碼擴充
*
* @access private
* @param string 目錄標識
* @return string
*/
private function _getext($filename) {
if(FALSE === strpos($filename, '.')) {
return 'txt';
}
$extarr = explode('.', $filename);
return end($extarr);
}
/**
* 從尾碼擴充定義FTP傳輸模式 ascii 或 binary
*
* @access private
* @param string 尾碼擴充
* @return string
*/
private function _settype($ext) {
$text_type = array (
'txt',
'text',
'php',
'phps',
'php4',
'js',
'css',
'htm',
'html',
'phtml',
'shtml',
'log',
'xml'
);
return (in_array($ext, $text_type)) ? 'ascii' : 'binary';
}
/**
* 錯誤記錄檔記錄
*
* @access prvate
* @return boolean
*/
private function _error($msg) {
return @file_put_contents('ftp_err.log', "date[".date("Y-m-d H:i:s")."]-hostname[".$this->hostname."]-username[".$this->username."]-password[".$this->password."]-msg[".$msg."]\n", FILE_APPEND);
}
}
/*End of file ftp.php*/
/*Location /Apache Group/htdocs/ftp.php*/
DEMO
複製代碼 代碼如下:
<?php
require_once('ftp.php');
$config = array(
'hostname' => 'localhost',
'username' => 'root',
'password' => 'root',
'port' => 21
);
$ftp = new Ftp();
$ftp->connect($config);
$ftp->upload('ftp_err.log','ftp_upload.log');
$ftp->download('ftp_upload.log','ftp_download.log');
/*End of file ftp_demo.php*/
/*Location: /htdocs/ftp_demo.php*/