Php upload file using sftp

Personally, I prefer avoiding the PECL SSH2 extension. My preferred approach involves phpseclib, a pure PHP SFTP implementation. Here's an example with phpseclib 2.0 (requires composer):

login('username', 'password')) {
    exit('Login Failed');
}

$sftp->put('remote.ext', 'local.ext', SFTP::SOURCE_LOCAL_FILE);
?>

Here's that same example with phpseclib 1.0:

login('username', 'password')) {
    exit('Login Failed');
}

$sftp->put('remote.ext', 'local.ext', NET_SFTP_LOCAL_FILE);
?>

One of the big things I like about phpseclib over the PECL extension is that it's portable. Maybe the PECL extension works on one version of Linux but not another. And on shared hosts it almost never works because it's hardly ever installed.

phpseclib is also, surprisingly, faster. And if you need confirmation that the file uploaded you can use phpseclib's built-in logging as proof.

Download and Extract the PHP library to the same directory level where the PHP script is to be used for connecting to a remote server via SFTP: https://sourceforge.net/projects/phpseclib

  • Below is the sample code to upload the files to a remote server.
login($username, $password))
{
    //directory path to source and destination
    $localDirectory = '/local_directory_name/';
    $remoteDirectory = '/remote_upload_directory_name/';
    //save all the filenames in the following array
    $filesToUpload = array();
    //Open the local directory and store all file names in $filesToUpload array
    if ($handle = opendir($localDirectory))
    {
        //loop the local source directory
        while (false !== ($file = readdir($handle)))
        {
            if ($file != '.' && $file != '..')
            {
                $filesToUpload[] = $file;
            }
        }

        closedir($handle);
    }
    //if source directory has any file to upload then upload all files to remote server
    if (!empty($filesToUpload))
    {
        //upload all the files to the remote server
        foreach ($filesToUpload as $file)
        {
            //Upload the local file to the remote server
            $success = $sftp->put($remoteDirectory . $file, $localDirectory . $file, NET_SFTP_LOCAL_FILE);
            if ($success)
            {
                echo '
uploaded ' . $file . '
'; } } } } else { exit('Login Failed'); } ?>
  • We can directly write texts on a remote file as well instead of uploading them from a local directory. Use below script for that.
login($username, $password) ) {
                //create file on remote server directory and with some text in it
                $success = $sftp->put(
                                      '/remote_upload_directory_name/file_name.text',
                                      'hello, world!'
                                      );
    } else {
        exit('Login Failed');
    }
?>

Notes:

  • phpseclib has pretty much zero server requirements. So long as the server supports PHP it’ll work.
  • ibssh2, in contrast, has to be installed on the server for it to work and a lot of servers don’t have it installed.
    IF it is installed you’re not going to need to include any additional files (whereas with phpseclib you will have to include them but that’s a big IF.

SFTP is a standard and secure protocol through which parties can safely transfer and share data and files. In any case, engaging with an SFTP server programatically can be challenging. In the following post, we’ll build a full fledged PHP based SFTP client, step by step, so that by the end of the guide, you’ll be able to do it all on your own!

Requirements

First and foremost, you'll need an SFTP server to connect to. If you don't have one, you can get an SFTP endpoint on SFTP To Go in less than 30 seconds.

PHP doesn’t come bundled with the SFTP libraries from the get-go, so we’ll need to install the required library ssh2 using PECL. The library relies on libssh2, so you must ensure that it is first installed on your machine. For example, on Ubuntu machines, run the following:

$ apt-get install libssh2-1-dev
$ pecl install -a ssh2-1.3

In accordance with the 12 factor app cloud development methodology, you should explicitly define your app’s dependencies, which would make our composer.json file look like this:

{
  "require": {
    "ext-ssh2": "*"
  }
}

Now the real fun begins!

Creating and dropping SFTP connections

We’ll begin by creating a new class that encapsulates the functionality required for our SFTP client. The public methods we first add allow us to connect to the server using either password authentication or key authentication, and provide the means to disconnect an existing session.

connection = @ssh2_connect($host, $port);
      if (! $this->connection)
        throw new Exception("Failed to connect to ${host} on port ${port}.");
    }

    // Login with user and password
    public function auth_password($username, $password)
    {
      if (! @ssh2_auth_password($this->connection, $username, $password))
        throw new Exception("Failed to authenticate with username $username " .
                            "and password.");

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

    // Disconnect session
    public function disconnect()
    {
      @ssh2_disconnect($this->connection);
    }
  }

?>
connect-class.php

To put our brand new methods to use, we’ll add a main function that instantiates our class and calls the auth_password function to start a connection, as well as close the connection with the disconnect function immediately after. We’ll use the environment variable SFTPTOGO_URL to obtain all the required information for connecting to an SFTP server in a URI format: sftp://user:password@host. Within our main function, the variable is parsed to extract the username, password, host and optional port.

auth_password($user, $pass);

    fwrite(STDOUT, "Disconnecting from [${host}] ...\n");
    $client->disconnect();

  }

  main();
?>
connect-main.php

Listing Files

Now that we have set up a successful connection, we can add a function to our class and use it to list files on the remote SFTP server. The listFiles function takes a $remote_dir argument and returns an array of file names, along with their size and modification timestamp. To call the function, simply pass a path (you can start with . to traverse the current directory content) and then process the array you get.

sftp;
    $realpath = ssh2_sftp_realpath($sftp, $remote_dir);
    $dir = "ssh2.sftp://$sftp$realpath";
    $tempArray = array();

    fwrite(STDOUT, "Listing [${remote_dir}] ...\n\n");

    if (is_dir($dir)) {
      if ($dh = opendir($dir)) {
        while (($file = readdir($dh)) !== false) {
          $realpath = ssh2_sftp_realpath($sftp, $file);
          $filetype = filetype($dir . $realpath);
          $modTime = "";
          $size = sprintf("%.2f", filesize($dir . $realpath));

          if($filetype == "dir") {
            $file = $file . "/";
            $modTime = "";
            $size = "PRE";
          }

          $tempArray[] = $file;

          printf("%19s %12s %s\n", $modTime, $size, $file);
        }
        closedir($dh);
      }
    }
    return $tempArray;
  }

?>
listfiles.php

Upload File

Now it’s time to upload a file. Use the uploadFile function and pass two arguments: the path to the local file to upload and the target remote path. A function call would look like this: $client->uploadFile("./local.txt", "./remote.txt");

sftp;
    $realpath = ssh2_sftp_realpath($sftp, $remote_file);
    $stream = @fopen("ssh2.sftp://$sftp$realpath", 'w');
    if (! $stream)
      throw new Exception("Could not open file: $realpath");
    $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);
  }
?>
uploadfile.php

Download File

Last but not least: Use the downloadFile function to download a file. Pass the path to the remote file and a local path in which the downloaded file will be stored, to the function. You would call the function like this: $client->downloadFile("./remote.txt", "./download.txt");

sftp;
    $realpath = ssh2_sftp_realpath($sftp, $remote_file);
    $stream = @fopen("ssh2.sftp://$sftp$realpath", 'r');
    if (! $stream)
      throw new Exception("Could not open file: $realpath");
    $contents = stream_get_contents($stream);
    file_put_contents ($local_file, $contents);
    @fclose($stream);
  }

  // Delete remote file
  public function deleteFile($remote_file){
    fwrite(STDOUT, "Deleting [${remote_file}] ...\n");
    $sftp = $this->sftp;
    $realpath = ssh2_sftp_realpath($sftp, $remote_file);
    unlink("ssh2.sftp://$sftp$realpath");
  }
?>
downloadfile.php

SFTP To Go with maximum security and reliability

SFTP To Go offers managed SFTP/FTPS/S3 as a service - maximum reliability, security, availability, with 1 minute setup. Great for companies of any size, any scale.

Php upload file using sftp

Php upload file using sftp

Check out SFTP To Go!

The Whole Thing

That’s it! If you would like to run the entire program from start to finish, copy the following code and save it as main.php:

connection = @ssh2_connect($host, $port);
    if (! $this->connection)
      throw new Exception("Failed to connect to ${host} on port ${port}.");
  }

  // Login with user and password
  public function auth_password($username, $password)
  {
    if (! @ssh2_auth_password($this->connection, $username, $password))
      throw new Exception("Failed to authenticate with username $username " .
                          "and password.");

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

  // Login with SSH agent
  public function auth_agent($username)
  {
    if (! @ssh2_auth_agent($this->connection, $username))
      throw new Exception("Failed to authenticate with username $username " .
                          "and public key.");

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

  // Disconnect session
  public function disconnect()
  {
    @ssh2_disconnect($this->connection);
  }
  
  // List remote directory files
  function listFiles($remote_dir) {
    $sftp = $this->sftp;
    $realpath = ssh2_sftp_realpath($sftp, $remote_dir);
    $dir = "ssh2.sftp://$sftp$realpath";
    $tempArray = array();

    fwrite(STDOUT, "Listing [${remote_dir}] ...\n\n");

    if (is_dir($dir)) {
      if ($dh = opendir($dir)) {
        while (($file = readdir($dh)) !== false) {
          $realpath = ssh2_sftp_realpath($sftp, $file);
          $filetype = filetype($dir . $realpath);
          $modTime = "";
          $size = sprintf("%.2f", filesize($dir . $realpath));

          if($filetype == "dir") {
            $file = $file . "/";
            $modTime = "";
            $size = "PRE";
          }

          $tempArray[] = $file;

          printf("%19s %12s %s\n", $modTime, $size, $file);
        }
        closedir($dh);
      }
    }

    return $tempArray;
  }

  // Upload local file to remote file
  public function uploadFile($local_file, $remote_file)
  {
    fwrite(STDOUT, "Uploading [${local_file}] to [${remote_file}] ...\n");

    $sftp = $this->sftp;
    $realpath = ssh2_sftp_realpath($sftp, $remote_file);
    $stream = @fopen("ssh2.sftp://$sftp$realpath", 'w');
    if (! $stream)
      throw new Exception("Could not open file: $realpath");
    $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);
  }

  // Download remote file to local file
  public function downloadFile($remote_file, $local_file)
  {
    fwrite(STDOUT, "Downloading [${remote_file}] to [${local_file}] ...\n");

    $sftp = $this->sftp;
    $realpath = ssh2_sftp_realpath($sftp, $remote_file);
    $contents = stream_get_contents($stream);
    if (! $stream)
      throw new Exception("Could not open file: $realpath");
    $contents = fread($stream, filesize("ssh2.sftp://$sftp$realpath"));
    file_put_contents ($local_file, $contents);
    @fclose($stream);
  }

  // Delete remote file
  public function deleteFile($remote_file){
    fwrite(STDOUT, "Deleting [${remote_file}] ...\n");
    $sftp = $this->sftp;
    $realpath = ssh2_sftp_realpath($sftp, $remote_file);
    unlink("ssh2.sftp://$sftp$realpath");
  }
}

function main()
{
  $raw_url = getenv('SFTPTOGO_URL');

  // Parse URL
  $parsed_url = parse_url($raw_url);

  if($parsed_url === false)
  {
    fwrite(STDERR, "Failed to parse SFTP To Go URL.\n");
    exit(1);
  }

  // Get user name and password
  $user = isset( $parsed_url['user'] ) ? $parsed_url['user'] : null;
  $pass = isset( $parsed_url['pass'] ) ? $parsed_url['pass'] : null;

  // Parse Host and Port
  $host = isset( $parsed_url['host'] ) ? $parsed_url['host'] : null;

  // Port is always 22
  $port = isset( $parsed_url['port'] ) ? $parsed_url['port'] : 22;

  fwrite(STDOUT, "Connecting to [${host}] ...\n");

  try
  {
    $client = new SFTPClient($host, $port);
    $client->auth_password($user, $pass);

    //*
    //* List working directory files
    //*
    $client->listFiles(".");

    //*
    //* Upload local file to remote file
    //*
    $client->uploadFile("./local.txt", "./remote.txt");

    //*
    //* Download remote file to local file
    //*
    $client->downloadFile("./remote.txt", "./download.txt");

    //*
    //* Delete remote file
    //*
    $client->deleteFile("./remote.txt");
  }
  catch (Exception $e)
  {
    echo $e->getMessage() . "\n";
  }
}

main();

?>
main.php

To run the code, use the command:

php main.php

You can also clone our repo or check out more code samples on Github.