PHP 8.1+SSH2 SFTP上传文件卡在15KB的问题求助
SFTP上传卡在15KB问题分析与解决(PHP 8.1.26 + IIS10 + SSH2 1.3.1)
问题概述
PHP从7.4升级到8.1.26(运行于IIS10)后,原有SFTP上传代码失效:
- 非空文件上传时进度卡在15KB处无法完成;
- 空文件可正常生成0KB文件,下载功能无异常;
- 手动用CoreFTP可正常上传,排除服务器端权限或配置问题;
- 使用SSH2扩展版本1.3.1,已确认无SCP权限。
测试验证:
- 有效逻辑:循环分块读取本地文件(4096字节缓冲区)并写入远程流,可完成上传;
- 无效逻辑:一次性读取整个文件内容后写入远程流(文件仅35KB,排除内存限制),上传卡住。
原因分析
- PHP 8.x流处理逻辑变更:PHP8.0+对资源流的处理机制有调整,ssh2扩展的SFTP流在一次性写入超过特定阈值(此处为15KB)的数据时,未正确处理SFTP协议的ACK交互,导致连接挂起。
- SSH2扩展兼容性瑕疵:你使用的ssh2 1.3.1版本发布于2019年,对PHP8.1的适配存在不足,尤其是Windows环境下的流写入逻辑,未跟上PHP8.x的流API变化。
- IIS环境IO管控:IIS对PHP进程的网络IO有特定管控,一次性大流量写入可能触发IO阻塞,分块写入的方式规避了该限制。
解决办法
1. 采用分块读写的稳定方案
继续使用已验证有效的分块读写逻辑,这是当前最可靠的解决方式,示例代码:
// 打开本地文件流 $localStream = fopen($localFilePath, 'r'); if (!$localStream) { die('无法打开本地文件'); } // 打开远程SFTP文件流 $remoteStream = fopen("ssh2.sftp://{$sftp}{$remoteFilePath}", 'w'); if (!$remoteStream) { fclose($localStream); die('无法打开远程文件'); } // 分块读写(4096字节缓冲区可按需调整) while ($buffer = fread($localStream, 4096)) { fwrite($remoteStream, $buffer); } // 关闭流并断开连接 fclose($localStream); fclose($remoteStream); ssh2_disconnect($connection);
2. 更新SSH2扩展至适配PHP8.1的版本
选择针对PHP8.x更新的ssh2扩展版本(需匹配Windows、PHP8.1的线程安全/非线程安全类型),新版本通常会修复与PHP8.x的兼容性问题,可能解决一次性写入的阻塞问题。
3. 调整PHP配置优化流处理
在php.ini中尝试调整以下配置:
- 增大
memory_limit(排除隐性内存限制); - 调整
default_socket_timeout为更大值,避免超时触发; - 确保
allow_url_fopen处于开启状态(默认开启)。
额外注意事项
- 避免使用
file_put_contents直接写入SFTP流,该方式本质为一次性写入,同样会触发阻塞; - 确认IIS的PHP进程有足够的网络IO权限,避免被防火墙或安全策略限制;
- 可开启SSH2调试回调,查看阻塞时的协议交互细节:
$callbacks = [ 'debug' => function ($message) { error_log("SFTP调试: {$message}"); } ]; $connection = ssh2_connect($sshHost, $sshPort, [], $callbacks);
内容的提问来源于stack exchange,提问作者Haskell McRavin
相关产品推荐
相关产品推荐

