PHP 日本大会 2024

ssh2_sftp

(PECL ssh2 >= 0.9.0)

ssh2_sftp初始化 SFTP 子系统

描述

ssh2_sftp(资源 $session): 资源|false

从已连接的 SSH2 服务器请求 SFTP 子系统。

参数

session

SSH 连接链接标识符,从 ssh2_connect() 调用获得。

返回值

此方法返回一个用于所有其他 ssh2_sftp_*() 方法和 ssh2.sftp:// fopen 包装器的 SSH2 SFTP 资源,或者在失败时返回 false

示例

示例 #1 通过 SFTP 打开文件

<?php
$connection
= ssh2_connect('shell.example.com', 22);
ssh2_auth_password($connection, 'username', 'password');

$sftp = ssh2_sftp($connection);

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

参见

添加笔记

用户贡献笔记 13 条笔记

49
David Barnes
18 年前
这是一个关于如何使用 SFTP 发送文件的示例

<?php

class SFTPConnection
{
private
$connection;
private
$sftp;

public function
__construct($host, $port=22)
{
$this->connection = @ssh2_connect($host, $port);
if (!
$this->connection)
throw new
Exception("无法连接到 $host 端口 $port.");
}

public function
login($username, $password)
{
if (! @
ssh2_auth_password($this->connection, $username, $password))
throw new
Exception("无法使用用户名 $username 和密码 $password 进行身份验证.");

$this->sftp = @ssh2_sftp($this->connection);
if (!
$this->sftp)
throw new
Exception("无法初始化 SFTP 子系统.");
}

public function
uploadFile($local_file, $remote_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'w');

if (!
$stream)
throw new
Exception("无法打开文件: $remote_file");

$data_to_send = @file_get_contents($local_file);
if (
$data_to_send === false)
throw new
Exception("无法打开本地文件: $local_file.");

if (@
fwrite($stream, $data_to_send) === false)
throw new
Exception("无法发送来自文件的数据: $local_file.");

@
fclose($stream);
}
}

try
{
$sftp = new SFTPConnection("localhost", 22);
$sftp->login("username", "password");
$sftp->uploadFile("/tmp/to_be_sent", "/tmp/to_be_received");
}
catch (
Exception $e)
{
echo
$e->getMessage() . "\n";
}

?>
6
Josh
9 年前
请注意,您必须输入完整包装器 URI 顺序才能使接受这些参数的函数正常工作。例如,这(在其他注释中多次引用)不起作用

while (false !== ($file = readdir($handle))) if (is_dir($file)) { /* ... */ }

这可以工作

$sc = ssh2_sftp(...);
while (false !== ($file = readdir($handle))) if (is_dir("ssh2.sftp://$sc/$file")) { /* ... */ }

您需要将“path”作为 fopen() 包装器 URI 传递给这些函数。
9
bas weerman
16 年前
我添加了一些用于扫描文件系统以及接收和删除文件的功能。

class SFTPConnection
{
private $connection;
private $sftp;

public function __construct($host, $port=22)
{
$this->connection = @ssh2_connect($host, $port);
if (! $this->connection)
throw new Exception("无法连接到 $host 端口 $port。");
}

public function login($username, $password)
{
if (! @ssh2_auth_password($this->connection, $username, $password))
throw new Exception("无法使用用户名 $username 和密码 $password 进行身份验证。");
$this->sftp = @ssh2_sftp($this->connection);
if (! $this->sftp)
throw new Exception("无法初始化 SFTP 子系统。");
}

public function uploadFile($local_file, $remote_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'w');
if (! $stream)
throw new Exception("无法打开文件: $remote_file");
$data_to_send = @file_get_contents($local_file);
if ($data_to_send === false)
throw new Exception("无法打开本地文件: $local_file。");
if (@fwrite($stream, $data_to_send) === false)
throw new Exception("无法发送来自文件的数据: $local_file。");
@fclose($stream);
}

函数 scanFilesystem($remote_file) {
$sftp = $this->sftp;
$dir = "ssh2.sftp://$sftp$remote_file";
$tempArray = array();
$handle = opendir($dir);
// 列出所有文件
while (false !== ($file = readdir($handle))) {
if (substr("$file", 0, 1) != "."){
if(is_dir($file)){
// $tempArray[$file] = $this->scanFilesystem("$dir/$file");
} else {
$tempArray[]=$file;
}
}
}
closedir($handle);
return $tempArray;
}

公共函数 receiveFile($remote_file, $local_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'r');
if (! $stream)
throw new Exception("无法打开文件: $remote_file");
$contents = fread($stream, filesize("ssh2.sftp://$sftp$remote_file"));
file_put_contents ($local_file, $contents);
@fclose($stream);
}

公共函数 deleteFile($remote_file){
$sftp = $this->sftp;
unlink("ssh2.sftp://$sftp$remote_file");
}
}
2
bas weerman
16 年前
我将读取函数更改为

公共函数 receiveFile($remote_file, $local_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'r');
if (! $stream)
throw new Exception("无法打开文件: $remote_file");
$size = $this->getFileSize($remote_file);
$contents = '';
$read = 0;
$len = $size;
while ($read < $len && ($buf = fread($stream, $len - $read))) {
$read += strlen($buf);
$contents .= $buf;
}
file_put_contents ($local_file, $contents);
@fclose($stream);
}

公共函数 getFileSize($file){
$sftp = $this->sftp;
return filesize("ssh2.sftp://$sftp$file");
}
2
Curtis Wyatt
16 年前
David Barnes提供的sftp类运行良好。但是,如果您收到关于fopen的错误以及它无法打开流的错误,请尝试使用远程服务器上的完全限定路径。

例如,如果您将文件上传到/Users/username/Sites/file.txt,这可能无法正常工作

<?php
try {
$sftp = new SFTPConnection("localhost", 22);
$sftp->login("username", "password");
$sftp->uploadFile("/tmp/to_be_sent", "Sites/file.txt");
}
catch (
Exception $e) {
echo
$e->getMessage() . "\n";
}
?>

但这样可以

<?php
try {
$sftp = new SFTPConnection("localhost", 22);
$sftp->login("username", "password");
$sftp->uploadFile("/tmp/to_be_sent", "/Users/username/Sites/file.txt");
}
catch (
Exception $e) {
echo
$e->getMessage() . "\n";
}
?>

不要假设因为您以该用户身份连接,所以您就从其主目录开始。

另一种可能的选择是,您需要先使用http://us.php.net/manual/en/function.ssh2-sftp-mkdir.php创建目录(如果它尚不存在),然后再将文件上传到其中。
1
Jaybee
10年前
上传(写入)文件时,必须使用绝对路径,而不是相对路径。

如果您像我一样想使用相对路径,您可以使用以下代码

$fh=fopen("ssh2.sftp://$sftp".ssh2_sftp_realpath($sftp,".")."/fileinmyhomedir.txt");
1
sandipshah at vthrive dot com
15年前


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

请确保您指定文件的“绝对”路径。

如果没有,您将收到类似的错误

“无法打开文件...”

原因很简单……ssh2.sftp://$sftp指向远程服务器上的“根”目录,而大多数情况下,用户无权访问该目录。

有必要将其指向您的“主”目录。例如,“ssh2.sftp://$sftp/home/username/filename”……其中“/home/username”是您的主目录。
2
webakiro at gmail dot com
8年前
如果您希望存储一次使用的协议+资源("ssh2.sftp://$sftp";)
有一个小技巧要知道……
这将不起作用

<?php
function connect(){
$connection = ssh2_connect('shell.example.com', 22);
ssh2_auth_password($connection, 'username', 'password');
$sftp = ssh2_sftp($connection);
return
"ssh2.sftp://$sftp";
}
$remote = connect();
is_file( $remote."/path/to/file");
// 警告
// 消息:is_file(): ## 不是有效的 SSH2 SFTP 资源
?>

您需要在函数(is_file、filesize、fopen……)使用它时仍然可以使用 $sftp。
否则,我猜 GC 会清理它并关闭 ssh2_stfp 连接。
这就是为什么这能工作

<?php
function connect(){
//...
global $sftp;
$sftp = ssh2_sftp($connection);
return
"ssh2.sftp://$sftp";
}
$remote = connect();
is_file( $remote."/path/to/file");

// 或者更好的方法:

class myClass {
public function
connect(){
//...
$this->sftp = ssh2_sftp($connection);
$this->remote = "ssh2.sftp://".$this->sftp;
}
public function
test(){
is_file( $this->remote."/path/to/file");
}
}
$obj = new myClass();
$obj->connect();
$obj->test();
?>
1
Jarrod Christman
7年前
David 的代码运行得非常好,只有一件事例外,在我将正斜杠放在 '$sftp' 之后,它才上传文件。

public function uploadFile($local_file, $remote_file)
{
$sftp = $this->sftp;
// 原版
// "ssh2.sftp://$sftp$remote_file"
$stream = @fopen("ssh2.sftp://$sftp/$remote_file", 'w');

if (! $stream)
throw new Exception("无法打开文件: $remote_file");

$data_to_send = @file_get_contents($local_file);
if ($data_to_send === false)
throw new Exception("无法打开本地文件: $local_file。");

if (@fwrite($stream, $data_to_send) === false)
throw new Exception("无法发送来自文件的数据: $local_file。");

@fclose($stream);
}
0
Youz
5年前
向之前的类添加一个函数,以使用密钥而不是用户名/密码进行连接

<?php
public function loginWithKey($username, $publicKey, $privateKey, $passphrase = null)
{
if (!@
ssh2_auth_pubkey_file($this->connection, $username, $publicKey, $privateKey, $passphrase)) {
throw new
Exception("无法使用给定的密钥或密码进行身份验证");
}

$this->sftp = @ssh2_sftp($this->connection);
if (!
$this->sftp) {
throw new
Exception("无法初始化 SFTP 子系统。");
}
}

?>
0
php at kanariepiet dot com
6年前
记住出于性能原因在资源上使用 stream_set_chunk_size(),尤其是在高延迟链接上传大型文件时。

SFTP 每次只能发送 32K 的数据,libssh2 将在发送的每个数据包后等待响应。因此,如果默认块大小为 8K,上传速度将非常慢。
如果将块大小设置为例如 1Mb,libssh2 将把该块分成多个 32K 的数据包发送,然后等待响应,从而使上传速度更快。
(有关更多详细信息,请参见 man libssh2_sftp_write)

<?php
$connection
= ssh2_connect('shell.example.com', 22);
ssh2_auth_password($connection, 'username', 'password');
$sftp = ssh2_sftp($connection);
$stream = fopen("ssh2.sftp://$sftp/path/to/file", 'w');

stream_set_chunk_size($stream, 1024*1024);
fwrite($stream, $data);
?>
0
briantafoya at briantafoya dot com
7年前
这里是一个修改过的 SFTPConnection 类,之前已经发布过,它返回一个递归目录扫描Filesystem方法。

<?php
class SFTPConnection
{
private
$connection;
private
$sftp;

public function
__construct($host, $port=22)
{
$this->connection = @ssh2_connect($host, $port);
if (!
$this->connection)
throw new
Exception("无法连接到 $host 端口 $port.");
}

public function
login($username, $password)
{
if (! @
ssh2_auth_password($this->connection, $username, $password))
throw new
Exception("无法使用用户名 $username 和密码 $password 进行身份验证.");
$this->sftp = @ssh2_sftp($this->connection);
if (!
$this->sftp)
throw new
Exception("无法初始化 SFTP 子系统.");
}

public function
uploadFile($local_file, $remote_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'w');
if (!
$stream)
throw new
Exception("无法打开文件: $remote_file");
$data_to_send = @file_get_contents($local_file);
if (
$data_to_send === false)
throw new
Exception("无法打开本地文件: $local_file.");
if (@
fwrite($stream, $data_to_send) === false)
throw new
Exception("无法发送来自文件的数据: $local_file.");
@
fclose($stream);
}

function
scanFilesystem($remote_file) {
$sftp = $this->sftp;
$dir = "ssh2.sftp://$sftp$remote_file";
$tempArray = array();

if (
is_dir($dir)) {
if (
$dh = opendir($dir)) {
while ((
$file = readdir($dh)) !== false) {
$filetype = filetype($dir . $file);
if(
$filetype == "dir") {
$tmp = $this->scanFilesystem($remote_file . $file . "/");
foreach(
$tmp as $t) {
$tempArray[] = $file . "/" . $t;
}
} else {
$tempArray[] = $file;
}
}
closedir($dh);
}
}

return
$tempArray;
}

public function
receiveFile($remote_file, $local_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'r');
if (!
$stream)
throw new
Exception("无法打开文件: $remote_file");
$contents = fread($stream, filesize("ssh2.sftp://$sftp$remote_file"));
file_put_contents ($local_file, $contents);
@
fclose($stream);
}

public function
deleteFile($remote_file){
$sftp = $this->sftp;
unlink("ssh2.sftp://$sftp$remote_file");
}
}
?>
0
duke1 at drakkon dot net
16 年前
如果有人对如何获取目录列表感兴趣
$SSH_CONNECTION= ssh2_connect('shell.example.com', 22);
ssh2_auth_password($SSH_CONNECTION, 'username', 'password');
//-------------------------------------------------------------------
//此函数查找给定目录中的所有文件并返回它们
function scanFilesystem($dir) {
$tempArray = array();
$handle = opendir($dir);
// 列出所有文件
while (false !== ($file = readdir($handle))) {
if (substr("$file", 0, 1) != "."){
if(is_dir($file)){
$tempArray[$file]=scanFilesystem("$dir/$file");
} else {
$tempArray[]=$file;
}
}
}
closedir($handle);
return $tempArray;
}

//-------------------------------------------------------------------
$sftp = ssh2_sftp($SSH_CONNECTION);

//获取所有 OUTGOING 文件列表的代码
$dir = "ssh2.sftp://$sftp/outgoing";
$outgoing = scanFilesystem($dir);
sort($outgoing);
print_r($outgoing);
To Top