How to connect to SFTP in Ruby on Rails

The following post will be dedicated to familiarizing our readers with writing Ruby on Rails code to connect and interact with SFTP servers. SFTP is a commonly used standard and secure protocol, with the direct goal of safe file and data transfer. When setting up a connection with the server, there are just a few steps to complete, and by the end of this post, you’ll be able to do so standing on your head.

Requirements

First things first, preparation. The `net-sftp` gem is required in order to connect and interact with an SFTP server in Ruby on Rails. When you are ready to install it, manually run:

gem install net-sftp

Or create a `Gemfile` file and declare your dependencies in it:

gem 'net-sftp'

Then run:

bundle install

Connecting to SFTP

In this post, we’ll be using an environment variable named SFTPTOGO_URL that contains all the information required to connect to an SFTP server in a URI format: sftp://user:password@host. The variable is parsed to extract the URI parts using URI.parse, and the remote server’s host key is verified by default using ~/.ssh/known_hosts.

Once the connection is established, the SFTP client object will be assigned to the instance variable @sftp_client.

require 'net/sftp'
require 'uri'

class SFTPClient
  def initialize(host, user, password)
    @host = host
    @user = user
    @password = password
  end

  def connect
    sftp_client.connect!
  rescue Net::SSH::RuntimeError
    puts "Failed to connect to #{@host}"
  end

  def disconnect
    sftp_client.close_channel
    ssh_session.close
  end


  def sftp_client
    @sftp_client ||= Net::SFTP::Session.new(ssh_session)
  end

  private

  def ssh_session
    @ssh_session ||= Net::SSH.start(@host, @user, @password)
  end

end

sftptogo_url = ENV['SFTPTOGO_URL']
begin
  uri = URI.parse(sftptogo_url)
rescue URI::InvalidURIError
  puts 'Bad SFTPTOGO_URL'
end

sftp = SFTPClient.new(uri.host, uri.user, password: uri.password)
sftp.connect


# disconnect
sftp.disconnect

Listing Files

Now that we have a working connection, we can use it to list files on the remote SFTP server. This is done by traversing the @sftp_client.dir.for_each method, which returns an array of objects that correspond to the files found in the path argument. In our example, our wrapper function simply prints out the files found in the remote path.

def list_files(remote_path)
  @sftp_client.dir.foreach(remote_path) do |entry|
    puts entry.longname
  end
end

Upload File

The next step is to upload a file. Use the @sftp_client object’s upload function and pass the path to the local file and the remote path (the same place the file should end up after we upload). A function call would look like this: @sftp_client.upload!("./local.txt", "./remote.txt")

def upload_file(local_path, remote_path)
  @sftp_client.upload!(local_path, remote_path)
  puts "Uploaded #{local_path}"
end

Download File

As we finalize the process, we just need to download our files. Use the @sftp_client object’s download function, and pass the path to the remote file and the local path in which to store the downloaded file. You would call the function like so: @sftp_client.download("./remote.txt", "./download.txt")

def download_file(remote_path, local_path)
  @sftp_client.download!(remote_path, local_path)
  puts "Downloaded #{remote_path}"
end

The Whole Thing

All done! If you would like to run the entire program from start to finish, copy the following code and save it as main.rb:

require 'net/sftp'
require 'uri'

class SFTPClient
  def initialize(host, user, password)
    @host = host
    @user = user
    @password = password
  end

  def connect
    sftp_client.connect!
  rescue Net::SSH::RuntimeError
    puts "Failed to connect to #{@host}"
  end

  def disconnect
    sftp_client.close_channel
    ssh_session.close
  end

  def upload_file(local_path, remote_path)
    @sftp_client.upload!(local_path, remote_path)
    puts "Uploaded #{local_path}"
  end

  def download_file(remote_path, local_path)
    @sftp_client.download!(remote_path, local_path)
    puts "Downloaded #{remote_path}"
  end

  def list_files(remote_path)
    @sftp_client.dir.foreach(remote_path) do |entry|
      puts entry.longname
    end
  end

  def sftp_client
    @sftp_client ||= Net::SFTP::Session.new(ssh_session)
  end

  private

  def ssh_session
    @ssh_session ||= Net::SSH.start(@host, @user, @password)
  end
end

sftptogo_url = ENV['SFTPTOGO_URL']
begin
  uri = URI.parse(sftptogo_url)
rescue URI::InvalidURIError
  puts 'Bad SFTPTOGO_URL'
end

sftp = SFTPClient.new(uri.host, uri.user, password: uri.password)
sftp.connect

# list files in directory
sftp.list_files('/path/to/remote')

# upload files
sftp.upload_file('/path/to/local', '/path/to/remote')

# download files
sftp.download_file('/path/to/remote', '/path/to/local')


# disconnect
sftp.disconnect

Finally, run it using the command:

ruby main.rb

Congratulations on connecting to SFTP using Ruby on Rails!

Cloud FTP with maximum security and reliability
SFTP To Go offers managed cloud storage service - highly available, reliable and secure. Great for companies of any size, any scale.
Try SFTP To Go for free!

Check out more code samples on Github.