PHP Conference Japan 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_*() 方法和 ssh2.sftp:// fopen 包裝器使用,或者在失敗時傳回 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("Could not connect to $host on port $port.");
}

public function
login($username, $password)
{
if (! @
ssh2_auth_password($this->connection, $username, $password))
throw new
Exception("Could not authenticate with username $username " .
"and password $password.");

$this->sftp = @ssh2_sftp($this->connection);
if (!
$this->sftp)
throw new
Exception("Could not initialize SFTP subsystem.");
}

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

if (!
$stream)
throw new
Exception("Could not open file: $remote_file");

$data_to_send = @file_get_contents($local_file);
if (
$data_to_send === false)
throw new
Exception("Could not open local file: $local_file.");

if (@
fwrite($stream, $data_to_send) === false)
throw new
Exception("Could not send data from file: $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")) { /* ... */ }

您需要將「路徑」作為 fopen() 包裝器 URI 傳遞給這些函數。
9
bas weerman
16 年前
我新增了一些用於掃描檔案系統、接收和刪除檔案的功能。

類別 SFTPConnection
{
私有 $connection;
私有 $sftp;

公開函數 __construct($host, $port=22)
{
$this->connection = @ssh2_connect($host, $port);
如果 (! $this->connection)
拋出新的例外("無法連線到 $host 的連接埠 $port。");
}

公開函數 login($username, $password)
{
如果 (! @ssh2_auth_password($this->connection, $username, $password))
拋出新的例外("無法使用使用者名稱 $username " . "和密碼 $password 進行驗證。");
$this->sftp = @ssh2_sftp($this->connection);
如果 (! $this->sftp)
拋出新的例外("無法初始化 SFTP 子系統。");
}

公開函數 uploadFile($local_file, $remote_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'w');
如果 (! $stream)
拋出新的例外("無法開啟檔案:$remote_file");
$data_to_send = @file_get_contents($local_file);
如果 ($data_to_send === false)
拋出新的例外("無法開啟本機檔案:$local_file。");
如果 (@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();
$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;
}

public function receiveFile($remote_file, $local_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'r');
如果 (! $stream)
拋出新的例外("無法開啟檔案:$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");
}
}
2
bas weerman
16 年前
我修改了讀取函式為

public function receiveFile($remote_file, $local_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'r');
如果 (! $stream)
拋出新的例外("無法開啟檔案:$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);
}

public function 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 時,需要確保 $sftp 仍然可用。
否則,我猜垃圾回收器會清除它並關閉 ssh2_stfp 連線。
這就是為什麼這樣可行

<?php
函式 connect(){
//...
全域 $sftp;
$sftp = ssh2_sftp($connection);
返回
"ssh2.sftp://$sftp";
}
$remote = connect();
is_file( $remote."/path/to/file");

// 或者更好的方法:

類別 myClass {
公開 函式
connect(){
//...
$this->sftp = ssh2_sftp($connection);
$this->remote = "ssh2.sftp://".$this->sftp;
}
公開 函式
test(){
is_file( $this->remote."/path/to/file");
}
}
$obj = new myClass();
$obj->connect();
$obj->test();
?>
1
Jarrod Christman
7 年前
David 的程式碼運作得非常好,除了一件事之外,它無法上傳檔案,直到我在 '$sftp' 後面加上一個正斜線。

公開函數 uploadFile($local_file, $remote_file)
{
$sftp = $this->sftp;
// 原始碼
// "ssh2.sftp://$sftp$remote_file"
$stream = @fopen("ssh2.sftp://$sftp/$remote_file", 'w');

如果 (! $stream)
拋出新的例外("無法開啟檔案:$remote_file");

$data_to_send = @file_get_contents($local_file);
如果 ($data_to_send === false)
拋出新的例外("無法開啟本機檔案:$local_file。");

如果 (@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
btafoya at briantafoya dot com
7 年前
這是先前發佈的修改版 SFTPConnection 類別,它會傳回一個遞迴目錄掃描 scanFilesystem 方法。

<?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("Could not connect to $host on port $port.");
}

public function
login($username, $password)
{
if (! @
ssh2_auth_password($this->connection, $username, $password))
throw new
Exception("Could not authenticate with username $username " . "and password $password.");
$this->sftp = @ssh2_sftp($this->connection);
if (!
$this->sftp)
throw new
Exception("Could not initialize SFTP subsystem.");
}

public function
uploadFile($local_file, $remote_file)
{
$sftp = $this->sftp;
$stream = @fopen("ssh2.sftp://$sftp$remote_file", 'w');
if (!
$stream)
throw new
Exception("Could not open file: $remote_file");
$data_to_send = @file_get_contents($local_file);
if (
$data_to_send === false)
throw new
Exception("Could not open local file: $local_file.");
if (@
fwrite($stream, $data_to_send) === false)
throw new
Exception("Could not send data from file: $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("Could not open file: $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);

//取得所有外寄檔案列表的程式碼
$dir = "ssh2.sftp://$sftp/outgoing";
$outgoing = scanFilesystem($dir);
sort($outgoing);
print_r($outgoing);
To Top