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
相关产品推荐
相关产品推荐

