Programming

While working on a project, I found myself having to download files using SFTP. This seemed like a fairly straightforward task and I thought PHP would surely have something that could help me along. A quick Google search provided me with ssh2_sftp to take a look at.

I work in Micro$oft shop, so the ssh2 library was, unfortunately, not immediately available without building my own. A huge thanks to Pierre Joye for providing compiled DLLs of common libraries for those of us stuck using MS. Finding the right DLL for your needs is just a Ctrl+F away on his list. Once the DLL was dropped in the proper directory and the web server restarted, we were in business.

Thankfully the comments, on the manual page for the ssh2_sftp function, provided me with some guidance to actually get started. What I discovered, was that the ssh2_sftp function creates a resource which allows us to create a stream that you can use with familiar functions like fopen. The function also lets us use the ssh2:// protocol wrapper (specifically, ssh2.sftp://), which you’ll see in the example. One of the neat things about the ssh2:// protocol wrapper is described in the manual:

In addition to accepting traditional URI login details, the ssh2 wrappers will also reuse open connections by passing the connection resource in the host portion of the URL.

This means, we can use the resource itself as part of our URL when opening our stream with fopen. You can see this in the example as well.

After tinkering around, I came up with some code that seemed to be a bit cleaner than the examples in the manual and with a bit more commenting, for explanation. Hopefully it will help to describe how you can list and download files over SFTP with the ssh2 library. This example uses basic password authentication to make an SFTP connection, grab the list of files and download them. Should you require authentication using a pubkey, check out ssh2_auth_pubkey_file.

<?php
// Make our connection
$connection = ssh2_connect('ftp.somehost.com');
 
// Authenticate
if (!ssh2_auth_password($connection, 'username', 'password')) {
    throw new Exception('Unable to connect.');
}
 
// Create our SFTP resource
if (!$sftp = ssh2_sftp($connection)) {
    throw new Exception('Unable to create SFTP connection.');
}
 
/**
  * Now that we have our SFTP resource, we can open a directory resource
  * to get us a list of files. Here we will use the $sftp resource in
  * our address string as I previously mentioned since our ssh2:// 
  * protocol allows it.
  */
$files = array();
$dirHandle = opendir("ssh2.sftp://$sftp/");
 
// Properly scan through the directory for files, ignoring directory indexes (. & ..)
while (false !== ($file = readdir($dirHandle))) {
    if ($file != '.' && $file != '..') {
        $files[] = $file;
    }
}
 
/**
  * Using our newly created list of files, we can go about downloading. We will
  * open a remote stream and a local stream and write from one to the other.
  * We will use error suppression on the fopen call to suppress warnings from
  * not being able to open the file.
  */
if (count($files)) {
    foreach ($files as $fileName) {
        // Remote stream
        if (!$remoteStream = @fopen("ssh2.sftp://$sftp/$fileName", 'r')) {
            throw new Exception("Unable to open remote file: $fileName");
        } 
 
        // Local stream
        if (!$localStream = @fopen("/localdir/$fileName", 'w')) {
            throw new Exception("Unable to open local file for writing: /localdir/$fileName");
        }
 
        // Write from our remote stream to our local stream
        $read = 0;
        $fileSize = filesize("ssh2.sftp://$sftp/$fileName");
        while ($read < $fileSize && ($buffer = fread($remoteStream, $fileSize - $read))) {
            // Increase our bytes read
            $read += strlen($buffer);
 
            // Write to our local file
            if (fwrite($localStream, $buffer) === FALSE) {
                throw new Exception("Unable to write to local file: /localdir/$fileName");
            }
        }
 
        // Close our streams
        fclose($localStream);
        fclose($remoteStream);
    }
}

There you have it! Hopefully this post was helpful in regards to assisting your understanding of how to use SFTP and the ssh2 library in PHP. If you liked this post, then please consider following me on Twitter or subscribing to my feed.

  • Anum

    It was a great help. Thanks a Bunch !!!

  • gary

    thanks for sharing!

  • Lester

    Works well thanks. However my php script hangs and won’t even exit() after using this method. Any thoughts?

  • Lester

    sorry e-mail incorrect should be .com

  • drokind

    I personally like phpseclib, a pure PHP SFTP implementation, better.

    More info:

    http://phpseclib.sourceforge.net/

    Doesn’t have any dependencies, making it ultra-portable, and, surprisingly, it’s actually faster than libssh2.

  • th3_w3b

    Great! It Works! Thanks!

  • jcrawford

    This is great but don’t expect to use it on directories with a large number of files (mine has 29,516). I am getting nothing but segmentation faults when trying to get a list of files on the remote server. Yea I know it’s not good to have that many files in a directory but that server is not under my control. Seems there is a deep issue either in PHP’s core or in php_ssh2 library.

  • Mauro Trentini

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

    PHP Warning: opendir(ssh2.sftp://Resource id #25/): failed to open dir: operation failed

    PHP Warning: readdir() expects parameter 1 to be resource, boolean given

    Fixed it following this link -> http://stackoverflow.com/questions/1466737/cant-get-sftp-to-work-in-php

  • Daniel Williams

    http://www.monstaftp.com offers SFTP/SCP as well as normal FTP through a web-based client that’s free to download. (disclaimer: I’m involved with this project)

  • Calin Blaga

    If getting:
    PHP Warning: opendir(): Unable to open ssh2.sftp://Resource id #5/outgoing on remote host
    check: stackoverflow.com/questions/1466737/cant-get-sftp-to-work-in-php#answer-42412427