Documentation Index Fetch the complete documentation index at: https://dylankenneally-react-native-ssh-sftp-96.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
The React Native SSH SFTP library provides robust file transfer capabilities with progress tracking and the ability to cancel ongoing transfers.
Downloading Files
Download files from the remote server to your local device:
const downloadedPath = await client . sftpDownload (
'/remote/path/to/file.txt' ,
'/local/path/to/save/'
);
console . log ( 'File downloaded to:' , downloadedPath );
Basic Download Example
import SSHClient from '@dylankenneally/react-native-ssh-sftp' ;
import RNFS from 'react-native-fs' ;
const downloadFile = async () => {
try {
const client = await SSHClient . connectWithPassword (
'10.0.0.10' ,
22 ,
'user' ,
'password'
);
const localPath = RNFS . DocumentDirectoryPath ;
const downloadedFile = await client . sftpDownload (
'/home/user/document.pdf' ,
localPath
);
console . log ( 'Downloaded to:' , downloadedFile );
client . disconnect ();
} catch ( error ) {
console . error ( 'Download failed:' , error );
}
};
import SSHClient from '@dylankenneally/react-native-ssh-sftp' ;
import RNFS from 'react-native-fs' ;
SSHClient . connectWithPassword (
'10.0.0.10' ,
22 ,
'user' ,
'password'
). then ( client => {
const localPath = RNFS . DocumentDirectoryPath ;
client . sftpDownload (
'/home/user/document.pdf' ,
localPath ,
( error , downloadedFile ) => {
if ( error ) {
console . error ( 'Download failed:' , error );
} else {
console . log ( 'Downloaded to:' , downloadedFile );
}
client . disconnect ();
}
);
});
Uploading Files
Upload files from your local device to the remote server:
await client . sftpUpload (
'/local/path/to/file.txt' ,
'/remote/path/to/destination/'
);
console . log ( 'File uploaded successfully' );
Basic Upload Example
import SSHClient from '@dylankenneally/react-native-ssh-sftp' ;
import RNFS from 'react-native-fs' ;
const uploadFile = async () => {
try {
const client = await SSHClient . connectWithPassword (
'10.0.0.10' ,
22 ,
'user' ,
'password'
);
const localFile = ` ${ RNFS . DocumentDirectoryPath } /photo.jpg` ;
const remotePath = '/home/user/uploads/' ;
await client . sftpUpload ( localFile , remotePath );
console . log ( 'Upload complete' );
client . disconnect ();
} catch ( error ) {
console . error ( 'Upload failed:' , error );
}
};
Progress Tracking
Monitor upload and download progress in real-time using event listeners.
Download Progress
Register the progress handler
Set up the DownloadProgress event listener before starting the download: client . on ( 'DownloadProgress' , ( event ) => {
console . log ( 'Download progress:' , event );
});
Start the download
await client . sftpDownload (
'/remote/large-file.zip' ,
RNFS . DocumentDirectoryPath
);
Upload Progress
Register the progress handler
Set up the UploadProgress event listener before starting the upload: client . on ( 'UploadProgress' , ( event ) => {
console . log ( 'Upload progress:' , event );
});
Start the upload
await client . sftpUpload (
` ${ RNFS . DocumentDirectoryPath } /large-file.zip` ,
'/remote/uploads/'
);
Complete Progress Example
Download with UI
Upload with UI
Both with Stats
import React , { useState } from 'react' ;
import { View , Text , Button , ProgressBarAndroid } from 'react-native' ;
import SSHClient from '@dylankenneally/react-native-ssh-sftp' ;
import RNFS from 'react-native-fs' ;
const FileDownloader = () => {
const [ progress , setProgress ] = useState ( 0 );
const [ downloading , setDownloading ] = useState ( false );
const downloadFile = async () => {
try {
setDownloading ( true );
const client = await SSHClient . connectWithPassword (
'10.0.0.10' ,
22 ,
'user' ,
'password'
);
// Track progress
client . on ( 'DownloadProgress' , ( event ) => {
if ( event && event . progress !== undefined ) {
setProgress ( event . progress );
}
});
await client . sftpDownload (
'/remote/large-file.zip' ,
RNFS . DocumentDirectoryPath
);
setProgress ( 100 );
console . log ( 'Download complete!' );
client . disconnect ();
} catch ( error ) {
console . error ( 'Download error:' , error );
} finally {
setDownloading ( false );
}
};
return (
< View >
< Button
title = "Download File"
onPress = { downloadFile }
disabled = { downloading }
/>
{ downloading && (
<>
< ProgressBarAndroid
styleAttr = "Horizontal"
progress = { progress / 100 }
/>
< Text > { progress . toFixed ( 1 ) } % </ Text >
</>
) }
</ View >
);
};
import React , { useState } from 'react' ;
import { View , Text , Button , ProgressBarAndroid } from 'react-native' ;
import SSHClient from '@dylankenneally/react-native-ssh-sftp' ;
import RNFS from 'react-native-fs' ;
const FileUploader = () => {
const [ progress , setProgress ] = useState ( 0 );
const [ uploading , setUploading ] = useState ( false );
const uploadFile = async () => {
try {
setUploading ( true );
const client = await SSHClient . connectWithPassword (
'10.0.0.10' ,
22 ,
'user' ,
'password'
);
// Track progress
client . on ( 'UploadProgress' , ( event ) => {
if ( event && event . progress !== undefined ) {
setProgress ( event . progress );
}
});
await client . sftpUpload (
` ${ RNFS . DocumentDirectoryPath } /large-file.zip` ,
'/remote/uploads/'
);
setProgress ( 100 );
console . log ( 'Upload complete!' );
client . disconnect ();
} catch ( error ) {
console . error ( 'Upload error:' , error );
} finally {
setUploading ( false );
}
};
return (
< View >
< Button
title = "Upload File"
onPress = { uploadFile }
disabled = { uploading }
/>
{ uploading && (
<>
< ProgressBarAndroid
styleAttr = "Horizontal"
progress = { progress / 100 }
/>
< Text > { progress . toFixed ( 1 ) } % </ Text >
</>
) }
</ View >
);
};
const [ stats , setStats ] = useState ({
bytesTransferred: 0 ,
totalBytes: 0 ,
percentage: 0 ,
speed: 0 // bytes per second
});
let lastUpdate = Date . now ();
let lastBytes = 0 ;
client . on ( 'DownloadProgress' , ( event ) => {
const now = Date . now ();
const timeDiff = ( now - lastUpdate ) / 1000 ; // seconds
const bytesDiff = event . bytesTransferred - lastBytes ;
const speed = timeDiff > 0 ? bytesDiff / timeDiff : 0 ;
setStats ({
bytesTransferred: event . bytesTransferred ,
totalBytes: event . totalBytes ,
percentage: ( event . bytesTransferred / event . totalBytes ) * 100 ,
speed: speed
});
lastUpdate = now ;
lastBytes = event . bytesTransferred ;
});
// Display helper functions
const formatBytes = ( bytes ) => {
const units = [ 'B' , 'KB' , 'MB' , 'GB' ];
let size = bytes ;
let unitIndex = 0 ;
while ( size >= 1024 && unitIndex < units . length - 1 ) {
size /= 1024 ;
unitIndex ++ ;
}
return ` ${ size . toFixed ( 2 ) } ${ units [ unitIndex ] } ` ;
};
// In your JSX:
< Text >
{ formatBytes ( stats . bytesTransferred ) } / { formatBytes ( stats . totalBytes ) }
( { stats . percentage . toFixed ( 1 ) } %)
</ Text >
< Text > Speed: { formatBytes ( stats . speed ) } /s </ Text >
Progress event handlers must be registered before initiating the transfer. The SFTP connection automatically registers progress listeners when established.
Canceling Transfers
You can cancel ongoing uploads and downloads at any time.
Cancel Download
client . sftpCancelDownload ();
Cancel Upload
client . sftpCancelUpload ();
Complete Cancellation Example
import React , { useState , useRef } from 'react' ;
import { View , Button , Text } from 'react-native' ;
import SSHClient from '@dylankenneally/react-native-ssh-sftp' ;
import RNFS from 'react-native-fs' ;
const FileTransferWithCancel = () => {
const [ progress , setProgress ] = useState ( 0 );
const [ transferring , setTransferring ] = useState ( false );
const clientRef = useRef ( null );
const startDownload = async () => {
try {
setTransferring ( true );
setProgress ( 0 );
const client = await SSHClient . connectWithPassword (
'10.0.0.10' ,
22 ,
'user' ,
'password'
);
clientRef . current = client ;
client . on ( 'DownloadProgress' , ( event ) => {
if ( event && event . progress !== undefined ) {
setProgress ( event . progress );
}
});
await client . sftpDownload (
'/remote/large-file.zip' ,
RNFS . DocumentDirectoryPath
);
console . log ( 'Download complete!' );
} catch ( error ) {
if ( error . message . includes ( 'cancel' )) {
console . log ( 'Download cancelled by user' );
} else {
console . error ( 'Download error:' , error );
}
} finally {
setTransferring ( false );
if ( clientRef . current ) {
clientRef . current . disconnect ();
clientRef . current = null ;
}
}
};
const cancelDownload = () => {
if ( clientRef . current && transferring ) {
clientRef . current . sftpCancelDownload ();
console . log ( 'Cancelling download...' );
}
};
return (
< View >
< Button
title = "Start Download"
onPress = { startDownload }
disabled = { transferring }
/>
< Button
title = "Cancel Download"
onPress = { cancelDownload }
disabled = { ! transferring }
/>
{ transferring && < Text > Progress: { progress . toFixed ( 1 ) } % </ Text > }
</ View >
);
};
Canceling a transfer will reject the promise with an error
Partially transferred files may remain on the filesystem
Always handle the cancellation error appropriately in your code
Working with Different File Types
Text Files
import RNFS from 'react-native-fs' ;
// Download
const filePath = await client . sftpDownload (
'/remote/config.json' ,
RNFS . DocumentDirectoryPath
);
// Read the content
const content = await RNFS . readFile ( filePath , 'utf8' );
const config = JSON . parse ( content );
Binary Files (Images, Videos, etc.)
import RNFS from 'react-native-fs' ;
import { Image } from 'react-native' ;
// Download image
const imagePath = await client . sftpDownload (
'/remote/photos/image.jpg' ,
RNFS . DocumentDirectoryPath
);
// Use in Image component
< Image source = { { uri: `file:// ${ imagePath } ` } } />
Large Files
For large files, always implement:
Progress tracking to show transfer status
Cancellation capability for user control
Error handling for network interruptions
Retry logic for failed transfers
const downloadLargeFile = async ( client , remotePath , localPath ) => {
let retries = 3 ;
while ( retries > 0 ) {
try {
await client . sftpDownload ( remotePath , localPath );
console . log ( 'Download successful' );
return ;
} catch ( error ) {
retries -- ;
if ( retries === 0 ) {
throw error ;
}
console . log ( `Download failed, retrying... ( ${ retries } attempts left)` );
await new Promise ( resolve => setTimeout ( resolve , 2000 ));
}
}
};
Batch Transfers
Transfer multiple files sequentially or in parallel:
const filesToDownload = [
'/remote/file1.txt' ,
'/remote/file2.txt' ,
'/remote/file3.txt'
];
for ( const remoteFile of filesToDownload ) {
try {
await client . sftpDownload (
remoteFile ,
RNFS . DocumentDirectoryPath
);
console . log ( `Downloaded: ${ remoteFile } ` );
} catch ( error ) {
console . error ( `Failed to download ${ remoteFile } :` , error );
}
}
Parallel Transfers (Multiple Clients)
const downloadFiles = async ( files ) => {
const downloads = files . map ( async ( file ) => {
const client = await SSHClient . connectWithPassword (
'10.0.0.10' ,
22 ,
'user' ,
'password'
);
try {
await client . sftpDownload ( file , RNFS . DocumentDirectoryPath );
console . log ( `Downloaded: ${ file } ` );
} finally {
client . disconnect ();
}
});
await Promise . all ( downloads );
};
const files = [
'/remote/file1.txt' ,
'/remote/file2.txt' ,
'/remote/file3.txt'
];
await downloadFiles ( files );
Best Practices
Always use absolute paths
// Good
await client . sftpDownload (
'/home/user/file.txt' ,
RNFS . DocumentDirectoryPath
);
// Avoid relative paths
await client . sftpDownload (
'file.txt' , // Where is this?
RNFS . DocumentDirectoryPath
);
Check available storage space
import RNFS from 'react-native-fs' ;
const freeSpace = await RNFS . getFSInfo ();
const fileInfo = await client . sftpLs ( '/remote' );
const targetFile = fileInfo . find ( f => f . filename === 'large-file.zip' );
if ( targetFile . fileSize > freeSpace . freeSpace ) {
console . error ( 'Insufficient storage space' );
return ;
}
await client . sftpDownload ( '/remote/large-file.zip' , RNFS . DocumentDirectoryPath );
Handle network interruptions
const downloadWithRetry = async ( remotePath , localPath , maxRetries = 3 ) => {
for ( let i = 0 ; i < maxRetries ; i ++ ) {
try {
return await client . sftpDownload ( remotePath , localPath );
} catch ( error ) {
if ( i === maxRetries - 1 ) throw error ;
console . log ( `Retry ${ i + 1 } / ${ maxRetries } ` );
await new Promise ( resolve => setTimeout ( resolve , 2000 ));
}
}
};
Clean up partial transfers
const downloadPath = ` ${ RNFS . DocumentDirectoryPath } /download.zip` ;
try {
await client . sftpDownload ( '/remote/file.zip' , RNFS . DocumentDirectoryPath );
} catch ( error ) {
// Remove partial download
if ( await RNFS . exists ( downloadPath )) {
await RNFS . unlink ( downloadPath );
}
throw error ;
}
Next Steps