| Title: | Robust SFTP Interface Using 'curl' |
|---|---|
| Description: | Provides a high-level, object-oriented interface for Secure File Transfer Protocol (SFTP) operations built upon the 'curl' package. The package implements an 'R6' class to manage persistent connections and provides 'tidyverse'-style functions for common file system tasks. Key features include recursive directory creation with idempotency support, "smart" local path resolution that distinguishes between files and directories, and the ability to download remote resources directly into memory as raw vectors for seamless integration into data processing pipelines. It is designed to handle common SFTP edge cases gracefully, providing informative error messages and robust path sanitization to ensure compatibility across different server configurations. |
| Authors: | Michael Kuo [aut, cre] |
| Maintainer: | Michael Kuo <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.0 |
| Built: | 2026-06-09 09:08:49 UTC |
| Source: | https://github.com/mikuo0628/sftpr |
SFTPConn R6 object that contains important connection
information safelyAn R6 class to safely store information needed for SFTP connection,
with convenient methods to check connections and existence of files or
directories, and create specific handles for sftp_* function family of
CRUD operations.
sftp_connect( protocol = "sftp", hostname = "localhost", path = NULL, port = "22", user = NA_character_, password = NA_character_, timeout = 30L, ..., .verbose = TRUE )sftp_connect( protocol = "sftp", hostname = "localhost", path = NULL, port = "22", user = NA_character_, password = NA_character_, timeout = 30L, ..., .verbose = TRUE )
protocol |
Character. Protocol string. Defaults to "sftp". |
hostname |
Character. Server URL or IP. Defaults to "localhost". |
path |
Character. Sub-path on server. |
port |
Character. Port number. Defaults to "22". |
user |
Character. SFTP account name. |
password |
Character. SFTP password. |
timeout |
Integer. Connection timeout. |
... |
Additional arguments passed to |
.verbose |
Logical. Defaults to |
One important goal of this design choice is to keep user credentials
safe, as private fields. Credentails are used to create specific handles
for sftp_* family, and are reused where approrpiate. SFTPConn
This class checks if credential is valid, and has a internal convenience
methods such as safe printing for basic information, checking destination
existence, and ensuring URL is correctly formatted.
SFTPConn R6 class object, used in sftp_* family.
if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create a new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = 2222, user = "tester", password = "password123" ) }if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create a new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = 2222, user = "tester", password = "password123" ) }
Deletes a specific file or directory from the remote server. If the target
is a directory, it must be empty unless .recursive = TRUE
is specified.
sftp_delete( sftp_conn, remote_url = NULL, .recursive = FALSE, .verbose = TRUE, .validate = TRUE )sftp_delete( sftp_conn, remote_url = NULL, .recursive = FALSE, .verbose = TRUE, .validate = TRUE )
sftp_conn |
An |
remote_url |
Character. The full URL or path of the file or directory to be operated on. |
.recursive |
Logical. Defaults to
|
.verbose |
Logical. Defaults to |
.validate |
Logical. Whether to validate the |
TRUE (invisibly) if the operation was successful.
Irreversibility: Deletion on SFTP is permanent. There is no "Trash" or "Recycle Bin" on most SFTP server configurations.
Recursive Caution: Setting .recursive = TRUE on a
high-level directory can result in significant data loss. Always verify
the remote_url before executing.
if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Delete a single file sftp_delete(sftp_conn, "project/old_report.csv") # Delete an entire directory and its contents sftp_delete(sftp_conn, "project/temp_outputs/", .recursive = TRUE) }if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Delete a single file sftp_delete(sftp_conn, "project/old_report.csv") # Delete an entire directory and its contents sftp_delete(sftp_conn, "project/temp_outputs/", .recursive = TRUE) }
Downloads a file from a remote SFTP server to a local disk location or directly into R's memory as a raw vector.
sftp_download( sftp_conn, remote_file, local_file = NA_character_, .create_dir = FALSE, .overwrite = FALSE, .verbose = TRUE, ... )sftp_download( sftp_conn, remote_file, local_file = NA_character_, .create_dir = FALSE, .overwrite = FALSE, .verbose = TRUE, ... )
sftp_conn |
An |
remote_file |
Character. The path or URL of the file on the SFTP server. |
local_file |
Character or
|
.create_dir |
Logical. Defaults to |
.overwrite |
Logical. Defaults to |
.verbose |
Logical. Defaults to |
... |
Additional arguments passed to |
If local_file is NULL, a raw vector of the file
contents. Otherwise, the resolved local path to the saved file (invisibly).
To provide a "smart" user experience, the function guesses
if local_file is intended to be a directory or a specific filename:
Directory Detection: If the path exists as a directory, ends
in a trailing slash, or has no file extension, it is treated as a folder.
The remote_file filename will be appended to this path.
File Detection: If the path does not exist and contains a file extension (e.g., ".csv"), it is treated as the final destination filename.
Ambiguity: In ambiguous cases (e.g., a non-existent path without a slash or extension), the function defaults to treating the path as a directory.
if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Download and use the remote filename sftp_download(sftp_conn, "data/raw_logs.zip") # Download to a specific local name sftp_download(sftp_conn, "remote_file.csv", "local_name.csv") # Download to memory for immediate processing raw_bytes <- sftp_download(sftp_conn, "data.json", local_file = NULL) # Parse with appropriate packages # data <- jsonlite::fromJSON(rawToChar(raw_bytes)) }if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Download and use the remote filename sftp_download(sftp_conn, "data/raw_logs.zip") # Download to a specific local name sftp_download(sftp_conn, "remote_file.csv", "local_name.csv") # Download to memory for immediate processing raw_bytes <- sftp_download(sftp_conn, "data.json", local_file = NULL) # Parse with appropriate packages # data <- jsonlite::fromJSON(rawToChar(raw_bytes)) }
Retrieves a directory listing from an SFTP server. If
.recursive = TRUE, it will perform a depth-first crawl of all
subdirectories found, implementing a path-tracking
algorithm to detect and skip circular symbolic links, preventing
infinite recursion and stack overflow errors.
sftp_list( sftp_conn = NULL, sftp_url = NULL, .verbose = TRUE, .recursive = FALSE )sftp_list( sftp_conn = NULL, sftp_url = NULL, .verbose = TRUE, .recursive = FALSE )
sftp_conn |
An |
sftp_url |
A SFTP URL of which the contents will be listed. If
|
.verbose |
Logical. Defaults to |
.recursive |
Logical. Defaults to
|
A data.frame containing remote file/directory metadata:
permission: Unix-style permission string (e.g., "drwxr-xr-x").
nlink: Number of hard links.
user: Owner username.
group: Owner group.
size: File size in bytes.
month, day, time_year: Timestamp components.
name: File or directory name.
type: Categorization as "dir" or "file".
url: The source URL for that specific object.
if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create a new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # List recursively sftp_list(sftp_conn, .recursive = TRUE) }if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create a new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # List recursively sftp_list(sftp_conn, .recursive = TRUE) }
This function creates directories on a remote server using the SFTP protocol.
It supports recursive directory creation, effectively behaving like
mkdir -p on a Unix-like system.
sftp_mkdir( sftp_conn, remote_url = NULL, .recursive = TRUE, .verbose = TRUE, .ignore_error = .recursive )sftp_mkdir( sftp_conn, remote_url = NULL, .recursive = TRUE, .verbose = TRUE, .ignore_error = .recursive )
sftp_conn |
An |
remote_url |
Character. The full URL or path of the file or directory to be operated on. |
.recursive |
Logical. Defaults to
|
.verbose |
Logical. Defaults to |
.ignore_error |
Logical. If |
When .recursive = TRUE, the function splits the path into segments and
attempts to create each one sequentially. It uses the * prefix for
internal calls to ensure that existing directories do not trigger errors.
invisible(TRUE) on success.
## Not run: if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Create a nested directory structure sftp_mkdir(sftp_conn, "project/data/results/2026", .recursive = TRUE) # Create a single directory and fail if parents are missing sftp_mkdir(sftp_conn, "simple_dir", .recursive = FALSE) } ## End(Not run)## Not run: if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Create a nested directory structure sftp_mkdir(sftp_conn, "project/data/results/2026", .recursive = TRUE) # Create a single directory and fail if parents are missing sftp_mkdir(sftp_conn, "simple_dir", .recursive = FALSE) } ## End(Not run)
Renames a file or directory on the SFTP server. This can also be used to move files between directories.
sftp_rename( sftp_conn, remote_url_from = NULL, remote_url_to = NULL, .recursive = FALSE, .verbose = TRUE )sftp_rename( sftp_conn, remote_url_from = NULL, remote_url_to = NULL, .recursive = FALSE, .verbose = TRUE )
sftp_conn |
An |
remote_url_from |
Character. The current path of the file or directory. |
remote_url_to |
Character. The new path for the file or directory. |
.recursive |
Logical. Defaults to
|
.verbose |
Logical. Defaults to |
The SFTP protocol's rename command is typically non-overwriting. If
remote_url_to already exists, the operation will fail.
When .recursive = TRUE, parent directories of remote_url_to
are identified, and existence ensured before attempting the renaming.
invisible(TRUE) on success.
if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Simple rename in the same folder sftp_rename(sftp_conn, "old_name.csv", "new_name.csv") # Move a file to a new, potentially non-existent directory sftp_rename( sftp_conn, "data/raw.csv", "archive/2026/processed.csv", .recursive = TRUE ) }if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Simple rename in the same folder sftp_rename(sftp_conn, "old_name.csv", "new_name.csv") # Move a file to a new, potentially non-existent directory sftp_rename( sftp_conn, "data/raw.csv", "archive/2026/processed.csv", .recursive = TRUE ) }
A robust wrapper to upload local files or data frames to a remote SFTP server. It manages the libcurl handle lifecycle, ensures connections are closed, and validates URLs against the connection's "Source of Truth".
sftp_upload( sftp_conn, local_file, remote_file = NULL, .create_dir = FALSE, .verbose = TRUE )sftp_upload( sftp_conn, local_file, remote_file = NULL, .create_dir = FALSE, .verbose = TRUE )
sftp_conn |
An |
local_file |
Character string (path to a file) or a |
remote_file |
Character string. The destination path on the server.
If |
.create_dir |
Logical. Defaults to |
.verbose |
Logical. Defaults to |
The function uses a secure lifecycle:
Validates the remote URL to prevent credential leakage.
Opens a file connection to the local source.
Uses on.exit to ensure file handles are released and
temporary files are unlinked even if the transfer is interrupted.
Returns TRUE (invisibly) on success. Throws an error on failure.
if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Upload `my_df` as csv sftp_upload(conn, my_df, "uploads/data.csv") }if (interactive() || Sys.getenv("R_SFTP_TEST_SERVER") == "true") { # Create new SFTP connection sftp_conn <- sftp_connect( hostname = "127.0.0.1", port = "2222", user = "tester", password = "password123" ) # Upload `my_df` as csv sftp_upload(conn, my_df, "uploads/data.csv") }