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

PHP使用ssh2_scp_recv获取SFTP文件失败及高效下载方案咨询

问题原因分析
  • 未过滤scandir返回的特殊目录项:scandir 执行后会默认返回 .(当前目录)、..(上级目录)两个特殊目录条目,代码直接对这两个条目调用 ssh2_scp_recv,函数会因为目标路径是目录而非文件执行失败,且代码未做任何错误校验,因此看不到异常提示,看起来就像函数没有执行。
  • 缺少路径与权限校验:一方面如果本地目录 /ftp_files 没有给PHP进程开放写入权限,ssh2_scp_recv 会直接写入失败;另一方面如果远程目录下存在子目录,ssh2_scp_recv 不支持直接下载目录,同样会执行失败。
  • 错误返回未处理:ssh2_scp_recv 执行失败时会返回布尔值 false,代码没有对返回结果做判断,所有执行错误都被静默忽略。
修复后的ssh2_scp_recv实现(无需phpseclib)
function get_sftp_content(){
    $url = 'someurl.com';
    $port = 22;
    $username = "user";
    $password = "password";

    $connection = ssh2_connect($url, $port);
    if (!$connection) throw new Exception('Unable to connect to server.');
    
    if (!ssh2_auth_password($connection, $username, $password)) throw new Exception('Authentication failed.');
 
    $sftp = ssh2_sftp($connection);
    if (!$sftp) throw new Exception('Unable to initialize SFTP resource.');

    $localDir = __DIR__ . '/ftp_files'; // 建议用绝对路径,避免相对路径出错
    // 先判断本地目录是否存在,不存在则创建
    if (!is_dir($localDir)) {
        mkdir($localDir, 0755, true);
    }
    $remoteDir = '/download';
    $remoteBasePath = 'ssh2.sftp://' . intval($sftp) . $remoteDir;

    $files = scandir($remoteBasePath);
    if (!empty($files)) {
        foreach ($files as $file) {
            // 过滤特殊目录和子目录
            if ($file == '.' || $file == '..' || is_dir($remoteBasePath . '/' . $file)) {
                continue;
            }
            $remoteFile = $remoteDir . '/' . $file;
            $localFile = $localDir . '/' . $file;
            $res = ssh2_scp_recv($connection, $remoteFile, $localFile);
            if (!$res) {
                echo "下载文件{$remoteFile}失败\n";
            }
        }
    }
    // 断开连接
    ssh2_disconnect($connection);
}

get_sftp_content();
更快的批量拉取替代方案
  • 流拷贝方案:如果不想用SCP协议,可以用ssh2_sftp打开远程文件流,调用原生stream_copy_to_stream函数做流拷贝,性能比手动循环fread高很多,代码示例:
    $remoteStream = fopen('ssh2.sftp://' . intval($sftp) . $remoteFile, 'rb');
    $localStream = fopen($localFile, 'wb');
    stream_copy_to_stream($remoteStream, $localStream);
    fclose($remoteStream);
    fclose($localStream);
    
  • 远程打包下载方案:如果你的SSH账号有权限执行 shell 命令,可以直接在远程将目标目录打包为单个压缩包,仅拉取压缩包到本地后解压,避免多次小文件传输的握手开销,是批量拉取大量小文件的最优方案,代码示例:
    // 远程执行打包命令
    $stream = ssh2_exec($connection, "cd /download && tar -zcf /tmp/download_package.tar.gz .");
    stream_set_blocking($stream, true);
    fclose($stream);
    // 拉取打包后的单个文件
    ssh2_scp_recv($connection, '/tmp/download_package.tar.gz', $localDir . '/download_package.tar.gz');
    // 本地解压,可根据实际环境用exec调用tar命令或者PHP的ZipArchive类处理
    
    该方案下100MB总大小的文件,全程耗时通常在30秒以内。

内容的提问来源于stack exchange,提问作者Sergio Rodrigues

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 19:45:05