无法让 SFTP 在 PHP 中工作

Posted

技术标签:

【中文标题】无法让 SFTP 在 PHP 中工作【英文标题】:Can't get SFTP to work in PHP 【发布时间】:2010-11-30 19:19:27 【问题描述】:

我正在用 php 编写一个简单的 SFTP 客户端,因为我们需要通过 n 个远程服务器以编程方式检索文件。我正在使用 PECL SSH2 扩展。

不过,我遇到了路障。 php.net 上的文档建议您可以这样做:

$stream = fopen("ssh2.sftp://$sftp/path/to/file", 'r');

但是,我有一个尝试类似的 ls 方法

public function ls($dir)

    $rd = "ssh2.sftp://$this->sftp/$dir";
    $handle = opendir($rd);
    if (!is_resource($handle)) 
        throw new SFTPException("Could not open directory.");
    

    while (false !== ($file = readdir($handle))) 
        if (substr($file, 0, 1) != '.')
            print $file . "\n";
        
    
    closedir($handle);

我收到以下错误:

PHP Warning:  opendir(): Unable to open ssh2.sftp://Resource id #5/outgoing on remote host

这很有意义,因为当您将资源转换为字符串时会发生这种情况。文档有错吗?我尝试用主机、用户名和主机替换资源,但也没有用。我知道路径是正确的,因为我可以从命令行运行 SFTP,它工作正常。

有没有其他人尝试将 SSH2 扩展与 SFTP 一起使用?我在这里遗漏了什么明显的东西吗?

更新:

我在内部的另一台机器上设置了 sftp,它工作得很好。因此,我尝试连接的服务器一定有问题。

【问题讨论】:

您能确认 ssh2 连接正常吗?使用其他 ssh2 函数怎么样? ssh2_connect() ssh2_auth_password() ssh2_sftp() 都正常工作。据我所知,我有一个有效的连接资源。 【参考方案1】:

这是我多年前发现的 ssh2 包中的一个错误,并在 php.net 上发布了一个补丁。它修复了这个问题,但需要重建 ssh2 pecl 包。您可以在此处阅读更多信息:https://bugs.php.net/bug.php?id=69981。我在包中的 ssh2_fopen_wrappers.c 文件中包含了一个补丁来解决这个问题。这是我包含的评论:

以下是 ssh2_fopen_wrappers.c 中导致此错误的代码行:(包括注释)

/*
       Find resource->path in the path string, then copy the entire string from the original path.
       This includes ?query#fragment in the path string
*/
    resource->path = estrdup(strstr(path, resource->path));

这行代码——因此也就是这个 bug——是作为 bug #59794 的修复而引入的。那行代码试图从路径变量中获取包含部分、查询和片段的字符串。 考虑路径变量的这个值:

ssh2.sftp://Resource id #5/topdir?a=something#heading1

当resource->path 为“/topdir”时,结果是“/topdir?a=something#heading1”被分配给resource->path,就像注释所说的那样。

现在考虑resource->path 为“/”的情况。这行代码执行后,resource->path就变成了“//Resource id#5/topdir#heading1”。这显然不是你想要的。下面是一行代码:

 resource->path = estrdup( strstr( strstr( path, "//" ) + 2, "/" ) );

在调用 php_url_parse() 之前,您可能还需要为 bug #73597 应用补丁,该补丁会从路径字符串中删除“Resource id #”。

【讨论】:

【参考方案2】:

当连接到 SFTP 服务器并且您需要连接到根文件夹(例如为了读取文件夹的内容)时,如果只使用“/”作为路径,您仍然会收到错误。

我找到的解决方案是使用路径“/./”,这是一个引用根文件夹的有效路径。当您登录的用户只能访问其自己的根文件夹并且没有完整路径可用时,这很有用。

所以在尝试读取根文件夹的内容时对服务器的请求应该是这样的:

$rd = "ssh2.sftp://$this->sftp/./";

【讨论】:

这是正确答案。在 PHP 5.2 和 5.4 之间的某个时候引入了一个错误,它破坏了对根目录的访问。感谢您提供解决方法! 不客气@BradenAnderson,我在这里找到了很多答案,我认为提供我所拥有的答案是公平的 在我意识到这就是我的情况之前,我忽略了这一点。感谢您发布此内容。 我终于有时间再试一次。首先,我必须关闭导致段错误的旧版本的扩展。一旦我把它照顾好,到目前为止似乎工作得很好!谢谢!【参考方案3】:

通过在 (Powershell) 服务器上启用 sftp 支持解决了我的问题

【讨论】:

【参考方案4】:

对于 php 版本 > 5.6.27 使用 intval()

$sftpConnection = ssh2_connect($host);
$sftp = ssh2_sftp($sftpConnection);
$fp = fopen("ssh2.sftp://" . intval($sftp) . $remoteFile, "r");

https://bugs.php.net/bug.php?id=73597

【讨论】:

赞成,这里也是。我猜 PHP 不理解它在新版本中的数据类型。 这也是我的解决方案,使用 PHP 7.2 php 版本升级后,我们可以登录/连接,但无法写入新文件。这对我们有帮助,谢谢【参考方案5】:

我的问题是,我在函数中连接并返回带有资源的字符串 URL。不幸的是,资源是在函数上下文中创建的,垃圾收集器在函数端断开资源。解决方案:通过引用返回资源并在更复杂的上下文中手动取消设置。

【讨论】:

【参考方案6】:

该页面上的文档包含错误。请查看此处的示例:http://php.net/ssh2_sftp - 您实际需要做的是在使用 fopen() 之前使用 ssh2_sftp() 打开一个特殊的 SFTP 资源。是的,它看起来就像那样,例如转换为字符串时的“资源 #24”……有点奇怪,但显然它可以工作。

另一个需要注意的是,SFTP 从根目录而不是远程用户的主目录开始,因此 URI 中的远程路径应该始终是绝对路径。

【讨论】:

【参考方案7】:

在不安装任何扩展等的情况下让 SFTP 在 PHP(甚至在 Windows 上)工作的最简单方法是 PHPSECLIB:http://phpseclib.sourceforge.net/。 SSH 的东西完全在 PHP 类中实现。

你使用的是这样的:

<?php
include('Net/SFTP.php');

$sftp = new Net_SFTP('www.domain.tld');
if (!$sftp->login('username', 'password')) 
    exit('Login Failed');


echo $sftp->pwd();
?>

【讨论】:

【参考方案8】:

我也遇到了同样的问题,但我可以找出问题所在。

在我的情况下,当连接到服务器时,我要去帐户的根目录,但由于服务器配置,我无法在那里写入。

我已使用 fireFTP 连接到该帐户,因此我可以看到该帐户的根在哪里...它是服务器的根。

我必须包含整个路径,直到允许我写入的文件夹,这样我才能解决问题。

所以,我的建议是使用图形界面获取路径(我使用过 fireFTP),并将整个路径添加到您的代码中。


$pathFromAccountRootFolderToMyDestinationFolder = '/Account/Root/Folder/To/My/Folder';
$stream = fopen("ssh2.sftp://".$sftp."/".$pathFromAccountRootFolderToMyDestinationFolder."/myFile.ext", 'r');

希望这将帮助您和其他有相同问题的人!

干杯!

【讨论】:

这是个好主意。我已经尝试过使用 FireZilla 和 WinSCP。不过,我用他们所拥有的作为绝对路径无济于事。 有没有办法获取用户主目录的真实路径?【参考方案9】:

我最近尝试在 PHP 上运行 SFTP,发现 phpseclib 更易于使用:

http://phpseclib.sourceforge.net/

如果您有幸不在共享主机上并且可以安装您想要的任何扩展,那么 PECL 扩展可能会更好,但并非我们所有人都这么幸运。另外,phpseclib 的 API 看起来更直观一点,是面向对象的。

【讨论】:

仅适用于某些 sftp 版本,在库 cmets 中有说明 99% 的 SFTP 安装使用 v3。实际上,我从来没有遇到过不支持它的 SFTP 服务器。许多客户端也只支持 v3。以 WinSCP 为例。当 v3 工作并且甚至没有人使用 v6 时添加对 v6 的支持只会浪费开发资源。 那你很幸运,但是当你必须为许多客户重写从 perl 到 php 的东西时,总是有 1% 的人使用不同的版本 也许您可以发布一个错误报告,然后要求支持更新版本。 phpseclib 开发人员似乎对这样的票很敏感。如果你不问,你将一无所获..【参考方案10】:

我遇到了类似的问题。我假设您正在做类似的事情:

    $dir = "ssh2.sftp://$sftp$remote_dir"; 
    $handle = opendir($dir);

$remote_dir 是根目录的完整路径时,open_dir 可以工作。如果 $remote_dir 只是 '/' 或 '',那么我会像你一样收到“无法打开”错误。

在我的例子中,似乎 ssh 连接在根文件夹,而不是像 ftp 那样连接到“主”目录。你提到它在不同的服务器上工作,所以我想知道这是否只是一个配置问题。

【讨论】:

就我所见,你是对的。它连接到根目录而不是“主目录”。使用命令行 sftp 客户端确实连接到“home”。我可能只需要从我尝试连接的客户端获取绝对路径。 $dir = 'ssh2.sftp://'.$sftp.'/'.$absolute_path; 使用绝对路径而不是主路径是答案。

以上是关于无法让 SFTP 在 PHP 中工作的主要内容,如果未能解决你的问题,请参考以下文章

无法让javascript和Jquery在functions.php中工作

是否可以使用 NAT 网络让 FTP 在 VirtualBox 中工作或让 php 的 ftp 功能工作

无法让 cURL 或 file_get_contents 在 PHP 中工作

无法让 subl 命令在 OSX 终端中工作 [重复]

无法让 DLookup 函数在 Access 2013 中工作

查询在 PHPMyAdmin 中工作但在 PHP 中不工作