PHP ftp_nb_continue() 函数

PHP ftp_nb_continue() 函数用于继续执行非阻塞的FTP操作,通常与 ftp_nb_get()ftp_nb_put() 等非阻塞函数配合使用。

重要:此函数用于在非阻塞模式下处理FTP传输,允许脚本在传输过程中执行其他操作。

语法

ftp_nb_continue(resource $ftp): int

参数说明

参数 描述
ftp 必需。FTP连接的标识符,由ftp_connect()ftp_ssl_connect()返回

返回值

  • FTP_FAILED (0) - 传输失败
  • FTP_FINISHED (1) - 传输完成
  • FTP_MOREDATA (2) - 传输仍在继续

使用场景

ftp_nb_continue() 主要用于以下非阻塞FTP函数:

  • ftp_nb_get() - 从FTP服务器下载文件(非阻塞)
  • ftp_nb_put() - 上传文件到FTP服务器(非阻塞)
  • ftp_nb_fget() - 从FTP服务器下载文件到已打开的文件(非阻塞)
  • ftp_nb_fput() - 将已打开的文件上传到FTP服务器(非阻塞)

示例

示例1:使用ftp_nb_get()下载文件(非阻塞)

<?php
// 连接FTP服务器
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";

$ftp_conn = ftp_connect($ftp_server) or die("无法连接到 $ftp_server");

// 登录
if (!ftp_login($ftp_conn, $ftp_user, $ftp_pass)) {
    die("登录失败");
}

// 启用被动模式(某些服务器需要)
ftp_pasv($ftp_conn, true);

// 开始非阻塞下载
$remote_file = "largefile.zip";
$local_file = "download.zip";

// 开始非阻塞下载,返回FTP_FAILED或FTP_MOREDATA
$result = ftp_nb_get($ftp_conn, $local_file, $remote_file, FTP_BINARY);

// 循环检查传输状态
while ($result == FTP_MOREDATA) {
    echo "正在下载... ";

    // 可以在这里执行其他任务
    // 例如:更新进度条、检查用户输入等

    // 继续传输
    $result = ftp_nb_continue($ftp_conn);

    // 添加短暂延迟,避免过度占用CPU
    usleep(100000); // 0.1秒
}

// 检查最终结果
if ($result == FTP_FINISHED) {
    echo "下载完成!文件已保存为: $local_file";
} elseif ($result == FTP_FAILED) {
    echo "下载失败!";
}

// 关闭连接
ftp_close($ftp_conn);
?>

示例2:带进度显示的非阻塞上传

<?php
// 简单的进度模拟函数
function showProgress($message) {
    static $counter = 0;
    $counter++;

    $progressChars = ['|', '/', '-', '\\'];
    $char = $progressChars[$counter % 4];

    echo "\r" . $message . " " . $char;
    flush(); // 立即输出
}

// 建立FTP连接
$ftp_conn = ftp_connect('localhost');
if ($ftp_conn === false) {
    die("无法连接FTP服务器");
}

if (!ftp_login($ftp_conn, 'user', 'pass')) {
    die("登录失败");
}

ftp_pasv($ftp_conn, true); // 启用被动模式

// 开始非阻塞上传
$local_file = "upload.txt";
$remote_file = "uploads/upload.txt";

// 检查本地文件是否存在
if (!file_exists($local_file)) {
    die("本地文件不存在: $local_file");
}

$result = ftp_nb_put($ftp_conn, $remote_file, $local_file, FTP_ASCII, FTP_NONBLOCK);

$start_time = time();
$max_timeout = 60; // 最大超时时间(秒)

while ($result == FTP_MOREDATA) {
    // 显示进度
    showProgress("正在上传文件...");

    // 检查是否超时
    if ((time() - $start_time) > $max_timeout) {
        echo "\n上传超时!";
        break;
    }

    // 继续传输
    $result = ftp_nb_continue($ftp_conn);

    // 短暂延迟
    usleep(50000); // 0.05秒
}

echo "\n"; // 换行

if ($result == FTP_FINISHED) {
    echo "上传成功!远程文件: $remote_file";
} elseif ($result == FTP_FAILED) {
    echo "上传失败!";
}

ftp_close($ftp_conn);
?>

示例3:批量处理多个文件(非阻塞)

<?php
class NonBlockingFTP {
    private $ftp_conn;
    private $active_transfers = [];

    public function connect($server, $username, $password) {
        $this->ftp_conn = ftp_connect($server);
        if (!$this->ftp_conn) {
            return false;
        }

        return ftp_login($this->ftp_conn, $username, $password);
    }

    public function downloadFiles($file_list) {
        $results = [];

        foreach ($file_list as $remote_file => $local_file) {
            // 开始非阻塞下载
            $result = ftp_nb_get($this->ftp_conn, $local_file, $remote_file, FTP_BINARY);

            if ($result == FTP_FAILED) {
                $results[$remote_file] = "开始下载失败";
            } else {
                $this->active_transfers[] = [
                    'remote' => $remote_file,
                    'local' => $local_file,
                    'status' => $result
                ];
                $results[$remote_file] = "下载已开始";
            }
        }

        return $results;
    }

    public function processTransfers() {
        $completed = [];
        $still_active = [];

        foreach ($this->active_transfers as $transfer) {
            if ($transfer['status'] == FTP_MOREDATA) {
                $result = ftp_nb_continue($this->ftp_conn);

                if ($result == FTP_FINISHED) {
                    $completed[] = $transfer['remote'];
                } elseif ($result == FTP_MOREDATA) {
                    $transfer['status'] = $result;
                    $still_active[] = $transfer;
                } else {
                    $still_active[] = $transfer; // 保持活动状态
                }
            } else {
                $still_active[] = $transfer;
            }
        }

        $this->active_transfers = $still_active;
        return $completed;
    }

    public function hasActiveTransfers() {
        return !empty($this->active_transfers);
    }

    public function close() {
        if ($this->ftp_conn) {
            ftp_close($this->ftp_conn);
        }
    }
}

// 使用示例
$ftp = new NonBlockingFTP();
if ($ftp->connect('localhost', 'user', 'pass')) {
    // 要下载的文件列表
    $files_to_download = [
        'server/file1.txt' => 'local/file1.txt',
        'server/file2.txt' => 'local/file2.txt',
        'server/file3.txt' => 'local/file3.txt'
    ];

    echo "开始批量下载...\n";
    $ftp->downloadFiles($files_to_download);

    // 处理传输
    $count = 0;
    $max_iterations = 100; // 防止无限循环

    while ($ftp->hasActiveTransfers() && $count < $max_iterations) {
        $completed = $ftp->processTransfers();

        if (!empty($completed)) {
            foreach ($completed as $file) {
                echo "完成: $file\n";
            }
        }

        // 模拟其他工作
        echo ".";
        $count++;
        usleep(200000); // 0.2秒延迟
    }

    echo "\n处理完成\n";
    $ftp->close();
} else {
    echo "连接失败\n";
}
?>

注意事项

  • 非阻塞操作需要循环调用ftp_nb_continue()直到传输完成
  • 在传输过程中,可以执行其他PHP代码,实现"伪异步"操作
  • 注意添加适当的延迟(usleep())以避免过度占用CPU
  • 非阻塞操作不会使脚本真正异步,只是允许在传输间隙执行其他代码
  • 某些FTP服务器可能对非阻塞操作的支持有限
  • 确保在处理过程中保持FTP连接有效

与非阻塞函数的区别

阻塞函数 非阻塞函数 特点
ftp_get() ftp_nb_get() 非阻塞版本需要配合ftp_nb_continue()使用
ftp_put() ftp_nb_put() 传输过程中可以执行其他操作
脚本会阻塞直到传输完成 脚本可以继续执行其他任务 适合大文件传输或需要用户交互的场景

相关函数

  • ftp_nb_get() - 非阻塞方式从FTP服务器下载文件
  • ftp_nb_put() - 非阻塞方式上传文件到FTP服务器
  • ftp_nb_fget() - 非阻塞方式下载文件到已打开的文件
  • ftp_nb_fput() - 非阻塞方式上传已打开的文件
  • ftp_get() - 阻塞方式下载文件
  • ftp_put() - 阻塞方式上传文件