You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

phpseclib SFTP上传成功率低 求助优化方案

提升phpseclib SFTP上传可靠性的优化方案

问题背景

最近从FTP切换到SFTP,向2台远程服务器上传1-4MB的数据文件时,PHP脚本上传成功率仅约50%,多日测试两台服务器均出现该问题。同一局域网内用FileZilla手动上传无报错,但SFTP的登录及目录列表速度比普通FTP慢。推测FileZilla的重试/超时机制比phpseclib更完善,现寻求phpseclib中提升此类场景上传可靠性的特性或代码方案。


现有代码及报错信息

上传脚本代码

<?php

$CodeRoot = dirname(__FILE__);
set_include_path(get_include_path() . PATH_SEPARATOR . $CodeRoot.'/phpseclib');
include($CodeRoot.'/phpseclib/Net/SFTP.php');

$RetryCount = 0;
$MaxRetryCount = 3;
do {  // 最多重试3次,成功则跳出循环
    $RetryCount = $RetryCount + 1;
    echoline("----- Upload Attempt  # ".$RetryCount."  to: $ftpServer");
    // 注:echoline函数仅用于将信息写入日志文件并在结束后发送邮件通知

    $ftp_conn11 = new NET_SFTP($ftpServer);
    if ($ftp_conn11){
        $ftp_login = $ftp_conn11->login($ftpUser, $ftpPassword);
        if (!$ftp_login) {
            echoline("Login Failed");
        }
        if ($ftp_login){
            if($ftp_conn11->put($remote_file, $local_file, NET_SFTP_LOCAL_FILE)){
                echoline("----- File uploaded successfully to: $ftpServer");
                $remoteSize = $ftp_conn11->size($remote_file);
                $localSize = filesize($local_file);
                if ($remoteSize == $localSize){
                    echoline("ALL OK ----- The FTP file size and LOCAL file sizes match");
                    $RetryCount = $MaxRetryCount + 2; // 跳出重试循环
                }
                if ($remoteSize != $localSize){
                    echoline("ERROR ----- The FTP file size and LOCAL file size does not match");
                }
            } else {
                $msg = "Cannot upload to ftp $ftpServer, Using user $ftpUser";
                echoline($msg);
            }
        }else{
            $msg = "Cannot login to FTP server: $ftpServer using user $ftpUser";
            echoline($msg);
        }
    }else{
        $msg = "Cannot connect to FTP server: $ftpServer";
        echoline($msg);
    }  // end of : if ($ftp_conn11)
    $ftp_conn11->disconnect();
    sleep(3);
} while ($RetryCount <= $MaxRetryCount);
echoline("---------------------------------------------------------------");
?>

报错日志

18:00:11 --- 第1次上传尝试 目标服务器:$TargetServer_1
PHP Notice: Expected SSH_FXP_STATUS in /config/www/folder1/phpseclib/Net/SFTP.php on line 2587
提示:预期SSH_FXP_STATUS,位于/config/www/folder1/phpseclib/Net/SFTP.php第2587行
18:00:36 --- 无法向FTP服务器$TargetServer_1上传,使用用户$userName
18:00:39 --- ---------------------------------------------------------------

18:00:39 --- 第2次上传尝试 目标服务器:$TargetServer_1
18:01:06 --- 文件成功上传至$TargetServer_1
18:01:07 --- 一切正常:远程文件与本地文件大小一致
18:01:10 --- ---------------------------------------------------------------

19:20:22 --- 第1次上传尝试 目标服务器:$TargetServer_2
PHP Notice: Expected SSH_FXP_STATUS in /config/www/folder1/phpseclib/Net/SFTP.php on line 2553
提示:预期SSH_FXP_STATUS,位于/config/www/folder1/phpseclib/Net/SFTP.php第2553行
19:20:52 --- 无法向FTP服务器$TargetServer_2上传,使用用户$userName
19:20:55 --- ---------------------------------------------------------------

19:20:55 --- 第2次上传尝试 目标服务器:$TargetServer_2
PHP Notice: Expected SSH_FXP_HANDLE or SSH_FXP_STATUS in /config/www/folder1/phpseclib/Net/SFTP.php on line 2400
提示:预期SSH_FXP_HANDLE或SSH_FXP_STATUS,位于/config/www/folder1/phpseclib/Net/SFTP.php第2400行
19:21:13 --- 无法向FTP服务器$TargetServer_2上传,使用用户$userName
19:21:16 --- ---------------------------------------------------------------

19:21:16 --- 第3次上传尝试 目标服务器:$TargetServer_2
PHP Notice: Expected SSH_FXP_STATUS in /config/www/folder1/phpseclib/Net/SFTP.php on line 2553
提示:预期SSH_FXP_STATUS,位于/config/www/folder1/phpseclib/Net/SFTP.php第2553行
19:21:43 --- 无法向FTP服务器$TargetServer_2上传,使用用户$userName
19:21:46 --- ---------------------------------------------------------------

19:21:46 --- 第4次上传尝试 目标服务器:$TargetServer_2
提示:预期SSH_FXP_HANDLE或SSH_FXP_STATUS,位于/config/www/folder1/phpseclib/Net/SFTP.php第2400行
19:22:04 --- 无法向FTP服务器$TargetServer_2上传,使用用户$userName
PHP Notice: Expected SSH_FXP_HANDLE or SSH_FXP_STATUS in /config/www/folder1/phpseclib/Net/SFTP.php on line 2400
19:22:07 --- ---------------------------------------------------------------

18:00:32 --- 第1次上传尝试 目标服务器:$TargetServer_2
18:00:50 --- 无法向FTP服务器$TargetServer_2上传,使用用户$userName
18:00:53 --- ---------------------------------------------------------------

18:00:53 --- 第2次上传尝试 目标服务器:$TargetServer_2
18:01:03 --- 登录失败
18:01:03 --- 无法登录至FTP服务器$TargetServer_2,使用用户$userName
18:01:07 --- ---------------------------------------------------------------

18:01:07 --- 第3次上传尝试 目标服务器:$TargetServer_2
18:01:23 --- 无法向FTP服务器$TargetServer_2上传,使用用户$userName
18:01:26 --- ---------------------------------------------------------------

18:01:26 --- 第4次上传尝试 目标服务器:$TargetServer_2
18:01:43 --- 无法向FTP服务器$TargetServer_2上传,使用用户$userName
18:01:46 --- ---------------------------------------------------------------

19:20:10 --- 第1次上传尝试 目标服务器:$TargetServer_1
PHP Notice: Expected SSH_FXP_STATUS in /config/www/folder1/phpseclib/Net/SFTP.php on line 2553
提示:预期SSH_FXP_STATUS,位于/config/www/folder1/phpseclib/Net/SFTP.php第2553行
19:20:47 --- 无法向FTP服务器$TargetServer_1上传,使用用户$userName
19:20:50 --- ---------------------------------------------------------------

19:20:50 --- 第2次上传尝试 目标服务器:$TargetServer_1
PHP Notice: Expected SSH_FXP_HANDLE or SSH_FXP_STATUS in /config/www/folder1/phpseclib/Net/SFTP.php on line 2400
提示:预期SSH_FXP_HANDLE或SSH_FXP_STATUS,位于/config/www/folder1/phpseclib/Net/SFTP.php第2400行
19:21:06 --- 无法向FTP服务器$TargetServer_1上传,使用用户$userName
19:21:09 --- ---------------------------------------------------------------

19:21:09 --- 第3次上传尝试 目标服务器:$TargetServer_1
PHP Notice: Expected SSH_FXP_STATUS in /config/www/folder1/phpseclib/Net/SFTP.php on line 2553
提示:预期SSH_FXP_STATUS,位于/config/www/folder1/phpseclib/Net/SFTP.php第2553行
19:21:24 --- 无法向FTP服务器$TargetServer_1上传,使用用户$userName
19:21:27 --- ---------------------------------------------------------------

19:21:27 --- 第4次上传尝试 目标服务器:$TargetServer_1
19:21:43 --- 文件成功上传至$TargetServer_1
19:21:43 --- 一切正常:远程文件与本地文件大小一致
19:21:46 --- ---------------------------------------------------------------

优化方案

1. 调整超时设置

phpseclib默认超时较短,针对网络较慢的SFTP服务器,手动设置连接、读写超时:

$ftp_conn11 = new NET_SFTP($ftpServer);
// 设置连接超时(单位:秒)
$ftp_conn11->setTimeout(30);
// 设置读写操作超时
$ftp_conn11->setTimeout(60);

2. 启用内置重试机制

phpseclib的SSH/SFTP类支持通过setRetryLimit设置单个操作的重试次数,针对SFTP命令失败自动重试:

$ftp_conn11->setRetryLimit(2); // 每个操作最多重试2次

3. 分块上传优化

默认的NET_SFTP_LOCAL_FILE模式是一次性传输,可手动实现分块上传,降低单次传输失败概率:

// 分块上传示例,每次上传100KB
$handle = fopen($local_file, 'rb');
$remoteHandle = $ftp_conn11->fopen($remote_file, 'w');
if ($remoteHandle) {
    while (!feof($handle)) {
        $data = fread($handle, 102400); // 100KB块
        $ftp_conn11->fwrite($remoteHandle, $data);
    }
    fclose($handle);
    $ftp_conn11->fclose($remoteHandle);
    // 验证文件大小
    $remoteSize = $ftp_conn11->size($remote_file);
    $localSize = filesize($local_file);
    if ($remoteSize == $localSize) {
        echoline("ALL OK ----- 远程文件与本地文件大小一致");
    } else {
        echoline("ERROR ----- 远程文件与本地文件大小不一致");
    }
}

4. 完善连接状态检查

在每次操作前检查连接是否存活,避免因连接断开导致的失败:

if (!$ftp_conn11->isConnected()) {
    $ftp_conn11->connect();
    $ftp_login = $ftp_conn11->login($ftpUser, $ftpPassword);
}

5. 升级phpseclib版本

旧版本的phpseclib可能存在SFTP协议处理的bug,升级到最新稳定版(如2.x或3.x分支)可修复已知的协议兼容问题。


内容的提问来源于stack exchange,提问作者Dave OBrien

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 11:25:54