Hướng dẫn dùng ssh2_sftp php trong PHP

(PECL ssh2 >= 0.9.0)

Show

    ssh2_sftpInitialize SFTP subsystem

    说明

    ssh2_sftp(resource $session): resource|false

    参数

    session

    An SSH connection link identifier, obtained from a call to ssh2_connect().

    返回值

    This method returns an SSH2 SFTP resource for use with all other ssh2_sftp_*() methods and the ssh2.sftp:// fopen wrapper, 或者在失败时返回 false.

    范例

    示例 #1 Opening a file via SFTP

    $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');
    ?>

    参见

    • ssh2_scp_recv() - Request a file via SCP
    • ssh2_scp_send() - Send a file via SCP

    David Barnes

    15 years ago

    Here is an example of how to send a file with SFTP:

    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";
    }
    ?>

    Josh

    7 years ago

    Note that you must enter the full wrapper URI order for functions that accept those parameters to work properly. For example, this (which is referenced more than once in other comments) does not work:

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

    This does work:

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

    You need to pass the "path" into these functions as an fopen() wrapper URI.

    bas weerman

    14 years ago

    I added some functionality for scanning the filesystem and receiving and deleting files.

    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();
                $handle = opendir($dir);
              // List all the files
                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');
            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");
        }
    }

    Curtis Wyatt

    14 years ago

    The sftp class provided by David Barnes works great.  However, if you get errors about fopen and it failing to open a stream, try the fully qualified path on the remote server.

    For example, if you are uploading a file to /Users/username/Sites/file.txt this may not work:

    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";
    }
    ?>

    but this will:

    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";
    }
    ?>

    Don't assume that since you are connecting as that user that you are starting in its home space.

    Another possible option is that you need to use http://us.php.net/manual/en/function.ssh2-sftp-mkdir.php first to make the directory if it does not exist already, and then upload the file into it.

    Youz

    3 years ago

    Adding a function to previous class to connect with key instead of user/password

    public function loginWithKey($username, $publicKey, $privateKey, $passphrase = null)
      {
        if (!@
    ssh2_auth_pubkey_file($this->connection, $username, $publicKey, $privateKey, $passphrase)) {
          throw new
    Exception("Could not authenticate with given keys or passphrase");
        }
    $this->sftp = @ssh2_sftp($this->connection);
        if (!
    $this->sftp) {
          throw new
    Exception("Could not initialize SFTP subsystem.");
        }
      }
    ?>

    btafoya at briantafoya dot com

    5 years ago

    Here is a modified SFTPConnection class previously posted that returns a recursive directory scanFilesystem method.

    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");
        }
    }
    ?>

    bas weerman

    14 years ago

    I changed the read function to:

        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");
            $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");
        }

    Jaybee

    8 years ago

    When uploading (writing) a file you must use an absolute path, not a relative one.

    If, like me, you want to use a relative path, you can use the following code:

    $fh=fopen("ssh2.sftp://$sftp".ssh2_sftp_realpath($sftp,".")."/fileinmyhomedir.txt");

    webakiro at gmail dot com

    6 years ago

    If you wish to store once the protocol + ressource used ("ssh2.sftp://$sftp";)
    There's a little trick to know...
    This won't work :

    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");
    // Warning
    // Message: is_file(): ## is not a valid SSH2 SFTP resource
    ?>

    You need to have $sftp still avaible at moment the function (is_file, filesize, fopen, ...) uses it
    Otherwise, I guess the GC cleans it up and closes the ssh2_stfp connection
    That's why this work :

    function connect(){
       
    //...
       
    global $sftp;
       
    $sftp = ssh2_sftp($connection);
        return
    "ssh2.sftp://$sftp";
    }
    $remote = connect();
    is_file( $remote."/path/to/file");// Or way better :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();
    ?>

    php at kanariepiet dot com

    4 years ago

    Remember to use stream_set_chunk_size() on the resource for performance reasons, especially if you're uploading large files on links with high latency.

    SFTP can only send 32K of data in one packet and libssh2 will wait for a response after each packet sent. So with a default chunk size of 8K the upload will be very slow.
    If you set the chunk size to for example 1Mb, libssh2 will send that chunk in multiple packets of 32K and then wait for a response, making the upload much faster.
    (see man libssh2_sftp_write for more details)

    $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);
    ?>

    Jarrod Christman

    5 years ago

    David's code works wonderfully except for one thing, it wouldn't upload a file until I placed a forward slash after '$sftp'

        public function uploadFile($local_file, $remote_file)
        {
            $sftp = $this->sftp;
            // Original
            // "ssh2.sftp://$sftp$remote_file"
            $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);
        }

    sandipshah at vthrive dot com

    12 years ago

    In

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

    please make sure that you are specifying the "absolute" path to a file.

    If not, you'll get errors like

    "Unable to open file ..."

    The reasoning is simple ... ssh2.sftp://$sftp points to the "root" directory on the remote server, where, most likely, one does not have access.

    It is necessary to point it to your "home" directory.  For example, "ssh2.sftp://$sftp/home/username/filename" ... where "/home/username" is where your home directory is.

    duke1 at drakkon dot net

    14 years ago

    if anyone is interested on how to get a directory listing:
    $SSH_CONNECTION= ssh2_connect('shell.example.com', 22);
    ssh2_auth_password($SSH_CONNECTION, 'username', 'password');
    //-------------------------------------------------------------------
    //this function finds all files within  given directory and returns them
    function scanFilesystem($dir) {
        $tempArray = array();
        $handle = opendir($dir);
      // List all the files
        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);

    //code to get listing of all OUTGOING files
    $dir = "ssh2.sftp://$sftp/outgoing";
    $outgoing = scanFilesystem($dir);
    sort($outgoing);
    print_r($outgoing);