Не могу заставить SFTP работать в PHP
Вопрос
Я пишу простой SFTP-клиент на PHP, потому что нам необходимо программно получать файлы через 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 на другой машине, и все работает нормально.Итак, что-то в сервере, к которому я пытаюсь подключиться, не работает.
Решение
При подключении к SFTP-серверу и необходимости подключения к корневой папке (например, для чтения содержимого папки) вы все равно получите сообщение об ошибке, если в качестве пути используете только «/».
Решение, которое я нашел, заключалось в использовании пути «/./», это действительный путь, ссылающийся на корневую папку.Это полезно, когда пользователь, с которым вы входите в систему, имеет доступ только к своей корневой папке и полный путь не доступен.
То есть запрос к серверу при попытке прочитать содержимое корневой папки должен быть примерно таким:
$rd = "ssh2.sftp://{$this->sftp}/./";
Другие советы
Для версий PHP > 5.6.27 используйте intval()
$sftpConnection = ssh2_connect($host);
$sftp = ssh2_sftp($sftpConnection);
$fp = fopen("ssh2.sftp://" . intval($sftp) . $remoteFile, "r");
У меня аналогичная проблема.Я предполагаю, что вы делаете что-то похожее на это:
$dir = "ssh2.sftp://{$sftp}{$remote_dir}";
$handle = opendir($dir);
Когда $remote_dir
тогда это полный путь от корня open_dir
работает.Если $remote_dir
это просто '/' или '', тогда я получаю ошибку «невозможно открыть», как и вы.
В моем случае кажется, что ssh подключается к корневой папке, а не к «домашнему» каталогу, как это делает ftp.Вы упомянули, что это работало на другом сервере, поэтому мне интересно, не проблема ли это просто в конфигурации.
Самый простой способ заставить 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();
?>
Документация на этой странице содержит ошибку.Вместо этого взгляните на пример здесь: http://php.net/ssh2_sftp — на самом деле вам нужно открыть специальный SFTP-ресурс с помощью ssh2_sftp() перед его использованием с fopen().И да, это выглядит именно так, например.«Ресурс № 24» при преобразовании в строку...немного странно, но, видимо, это работает.
Еще одно предостережение: SFTP запускается в корневом каталоге, а не в домашнем каталоге удаленного пользователя, поэтому ваш удаленный путь в URI всегда должен быть абсолютным.
У меня была такая же проблема, но я смог понять проблему.
В моем случае при подключении к серверу я заходил в корень аккаунта и из-за конфигов сервера не мог туда писать.
Я подключился к учетной записи с помощью FireFTP и мог видеть, где находится корень учетной записи... это был корень сервера.
Мне пришлось указать весь путь до папки, в которую мне разрешено писать, чтобы я мог решить проблему.
Итак, мой совет — получить путь с помощью графического интерфейса (я использовал fireFTP) и добавить весь путь в свой код.
$pathFromAccountRootFolderToMyDestinationFolder = '/Account/Root/Folder/To/My/Folder';
$stream = fopen("ssh2.sftp://".$sftp."/".$pathFromAccountRootFolderToMyDestinationFolder."/myFile.ext", 'r');
Надеюсь, это поможет вам и другим людям с той же проблемой!
Ваше здоровье!
Недавно я попытался заставить работать SFTP на PHP и обнаружил, что phpseclib намного проще в использовании:
http://phpseclib.sourceforge.net/
Если вы можете позволить себе роскошь не находиться на общем хосте и можете установить любые расширения, которые захотите, возможно, расширение PECL будет лучше, но не всем нам так повезло.Кроме того, API phpseclib выглядит немного более интуитивно понятным, поскольку является ООП и все такое.
Моя проблема заключалась в том, что я подключался к функции и возвращал URL-адрес строки с ресурсом внутри.К сожалению, ресурс создается в контексте функции, а сборщик мусора отключает ресурс при завершении функции.Решение:вернуть ресурс по ссылке и отключить его вручную в более сложном контексте.
Решил мою проблему, включив поддержку sftp на сервере (Powershell).
Это ошибка в пакете 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));
Эта строка кода (и, следовательно, эта ошибка) была введена как исправление ошибки № 59794.Эта строка кода пытается получить строку, содержащую часть, запрос и фрагмент, из переменной пути.Рассмотрим это значение для переменной пути:
ssh2.sftp://Resource id #5/topdir?a=something#heading1
Когда путь ресурса-> равен «/topdir», в результате «/topdir?a=something#heading1» присваивается пути ресурса->, как сказано в комментарии.
Теперь рассмотрим случай, когда ресурс->путь равен «/».После выполнения строки кода путь resources-> становится "//Resource id#5/topdir#heading1".Это явно не то, что вы хотите.Вот строка кода, которая делает:
resource->path = estrdup( strstr( strstr( path, "//" ) + 2, "/" ) );
Вам также может потребоваться применить исправление для ошибки № 73597, которое удаляет «Идентификатор ресурса №» из строки пути перед вызовом php_url_parse().