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