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!
Check out more code samples on Github.