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

PHP执行SFTP命令下载文件的实现问题及方案咨询

SFTP自动下载PHP实现问题

我的数据供应商仅提供SFTP连接用于下载文件,SFTP采用SSH-RSA公私钥认证,相关配置均已正确设置。我通过以下两条命令手动执行SSH已成功下载到文件:

sftp -o PORT=6010 JJEE@XX.160.XX.XX
Connected to JJEE@XX.160.XX.XX
sftp> get /BACKUP/01December2021.zip

为了实现自动下载,我尝试使用PHP编写脚本,先后测试了两种实现方案:

方案1

<?php
if (!function_exists("ssh2_connect")) 
    {die("function ssh2_connect doesn't exist");}

$connection = ssh2_connect("XX.160.XX.XX","6010",array('hostkey'=>'ssh-rsa'),array());
$connect=ssh2_auth_pubkey_file($connection,'JJEE','/location/id_rsa.pub','/location/id_rsa','');

if ($connect)
    {echo "Public Key Authentication Successful <br/>";}
else
    {echo "Public Key Authentication failed <br/>";}
?>

这个简单脚本运行时报错Public Key Authentication failed,同时触发PHP警告PHP Warning: ssh2_auth_pubkey_file(): Authentication failed for JPL_EOD using public key。我已确认id_rsa.pub和id_rsa的文件权限配置正确。
我推测是供应商仅开放了SFTP权限,导致我无法通过ssh2_connect创建SSH连接。我看到很多推荐使用phpseclib的方案,但我不想只为了两行逻辑引入第三方类库,因此尝试了第二种方案。

方案2

<?php
passthru('sftp -o PORT=6010 JJEE@XX.160.XX.XX && get /BACKUP/01December2021.zip');
?>

我在终端执行php /location-to-script/method-2.php运行该脚本时,能看到Connected to JJEE@XX.160.XX.XX的输出,但后续的get命令被识别为SSH命令而非SFTP子命令,导致下载失败。

咨询问题

  1. 登录SFTP后,如何正确发送get指令作为SFTP命令完成文件下载?
  2. 如果需要连续执行多条SFTP命令,应该如何实现?
  3. 是否有其他更优的实现方案?

问题解答

1. 单条SFTP命令执行方法

你方案2失败的原因是&&是Shell逻辑运算符,会先等待前面的sftp命令执行结束(退出交互模式)后才会执行后面的get指令,自然会被识别为系统命令而非SFTP子命令。
正确的做法是将SFTP指令通过标准输入传递给sftp进程,写法如下:

<?php
// 单条命令直接通过字符串传递
passthru('echo "get /BACKUP/01December2021.zip" | sftp -o PORT=6010 JJEE@XX.160.XX.XX');
?>

也可以使用Shell的Here字符串语法简化写法:

passthru('sftp -o PORT=6010 JJEE@XX.160.XX.XX <<< "get /BACKUP/01December2021.zip"');

2. 多条SFTP命令连续执行方法

如果需要执行多条命令,可以用Here文档的方式批量传递指令:

<?php
$sftp_cmd = <<<EOF
cd /BACKUP
get 01December2021.zip
get 02December2021.zip
ls
quit
EOF;
passthru('sftp -o PORT=6010 JJEE@XX.160.XX.XX << EOF
'.$sftp_cmd.'
EOF');
?>

所有写在EOF之间的指令都会被当做SFTP子命令按顺序执行,不需要额外的交互逻辑。

3. 更优实现方案

  • 方案A:curl调用
    大部分服务器环境默认预装了curl,curl原生支持sftp协议,一行命令即可完成下载,不需要处理交互逻辑:
    <?php
    passthru('curl -u JJEE: --key /location/id_rsa --pubkey /location/id_rsa.pub sftp://XX.160.XX.XX:6010/BACKUP/01December2021.zip -o /本地保存路径/01December2021.zip');
    ?>
    
    这个方案稳定性更高,错误返回也更规范,方便后续加错误判断逻辑。
  • 方案B:ssh2扩展兼容调整
    你之前ssh2扩展认证失败大概率不是供应商限制SSH连接,而是老版本ssh2扩展默认使用的RSA-SHA1算法被服务端禁用了,可以在连接时指定兼容的加密算法尝试修复:
    $methods = [
        'hostkey' => 'ssh-rsa',
        'kex' => 'diffie-hellman-group-exchange-sha256',
        'client_to_server' => [
            'crypt' => 'aes256-ctr,aes192-ctr,aes128-ctr',
            'mac' => 'hmac-sha2-256,hmac-sha1'
        ],
        'server_to_client' => [
            'crypt' => 'aes256-ctr,aes192-ctr,aes128-ctr',
            'mac' => 'hmac-sha2-256,hmac-sha1'
        ]
    ];
    $connection = ssh2_connect("XX.160.XX.XX","6010",$methods);
    

所有方案都需要注意:PHP脚本的运行用户(通常是www-data、apache或者php-fpm运行用户)需要有私钥文件的读取权限,以及本地下载目录的写入权限。


内容的提问来源于stack exchange,提问作者jahajee.com

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 11:36:05