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

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,排除内存限制),上传卡住。

原因分析

  1. PHP 8.x流处理逻辑变更:PHP8.0+对资源流的处理机制有调整,ssh2扩展的SFTP流在一次性写入超过特定阈值(此处为15KB)的数据时,未正确处理SFTP协议的ACK交互,导致连接挂起。
  2. SSH2扩展兼容性瑕疵:你使用的ssh2 1.3.1版本发布于2019年,对PHP8.1的适配存在不足,尤其是Windows环境下的流写入逻辑,未跟上PHP8.x的流API变化。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:55:57