Provides more reliable file download than Net_FTP from PEAR
*/
class SolidFTP extends Net_FTP
{
/**
* Maximum number of retries for file download
* @var integer
*/
var $_retry_count = 20;
/**
* Last working directory on the remote server
* @var string
*/
var $_last_dir = null;
/**
* More reliable file download from the FTP-server
* @see Net_FTP::get()
*
* @param string $remote_file - The absolute or relative path to the file to download
* @param string $local_file - The local file to put the downloaded in
* @param bool $overwrite - Whether to overwrite existing file (optional)
* @param int $mode - Either FTP_ASCII or FTP_BINARY (optional)
* @return mixed - true on success, otherwise PEAR::Error
*/
function get( $remote_file, $local_file, $overwrite = false, $mode = null)
{
if (!isset($mode)) {
$mode = $this->checkFileExtension( $remote_file);
}
$remote_file = $this->_constructPath($remote_file);
if (@file_exists($local_file) && !$overwrite) {
return $this->raiseError( "Local file '$local_file' exists and may not be overwriten.",
NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN);
}
if (@file_exists($local_file) && !@is_writeable($local_file) && $overwrite)
{
return $this->raiseError( "Local file '$local_file' is not writeable. Can not overwrite.",
NET_FTP_ERR_OVERWRITELOCALFILE_FAILED);
}
// Remember a working directory
$this->_last_dir = $this->pwd();
if ($this->isError( $this->_last_dir)) {
$this->_last_dir = null;
}
$resume_pos = 0;
$get_result = $reconn_result = false;
for ($current_try = 0; $current_try <= $this->_retry_count; $current_try++)
{
if ($current_try > 0) {
// Try to reconnect
$reconn_result = $this->reconnect();
if ($this->isError( $reconn_result, NET_FTP_ERR_DIRCHANGE_FAILED)) {
// Working directory does not exist anymore
return $reconn_result;
} elseif ($this->isError( $reconn_result)) {
// Other connection error may be temporary
continue;
}
clearstatcache();
$resume_pos = filesize( $local_file);
}
// Check if remote file still exists
if (@ftp_size( $this->_handle, $remote_file) == -1) {
return $this->raiseError( "Remote file '$remote_file' does not exist.",
NET_FTP_ERR_NOREMOTEFILE);
}
// Transfer a chunk of file
// ftp_get() deletes local file on failure and thus cannot be used
$get_result = @ftp_nb_get( $this->_handle, $local_file, $remote_file, $mode, $resume_pos);
while ($get_result == FTP_MOREDATA) {
$this->_announce('nb_get');
$get_result = @ftp_nb_continue( $this->_handle);
}
// Transfer successful
if ($get_result) break;
}
if (!$get_result || $this->isError( $reconn_result)) {
return $this->raiseError( "Retry count exceeded while downloading '$remote_file' - giving up.",
NET_FTP_ERR_RETRYCOUNTEXCEEDED);
} else {
return true;
}
}
/**
* Reconnect to the remote FTP-server, using last hostname,
* username, password and working directory
* @return mixed - true on success, otherwise PEAR::Error
*/
function reconnect() {
$this->_announce('reconnect');
$res = $this->connect();
if ($this->isError( $res)) return $res;
$res = $this->login();
if ($this->isError( $res)) return $res;
if (!is_null( $this->_last_dir)) {
$res = $this->cd( $this->_last_dir);
if ($this->isError( $res)) return $res;
}
return true;
}
/**
* Set maximum number of retries
* @param integer $retry_count
* @return mixed - true on success, otherwise PEAR::Error
*/
function setRetryCount( $retry_count) {
if (!is_int( $retry_count) || ($retry_count < 0)) {
return $this->raiseError( 'Retry count '.$timeout.' is invalid, has to be an integer >= 0',
NET_FTP_ERR_RETRYCOUNTLESSZERO);
}
$this->_retry_count = $retry_count;
return true;
}
/**
* Return maximum number of retries
* @return integer
*/
function getRetryCount() {
return $this->_retry_count;
}
} // class SolidFTP
?>