Skip to main content
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);
  }
};

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

1

Register the progress handler

Set up the DownloadProgress event listener before starting the download:
client.on('DownloadProgress', (event) => {
  console.log('Download progress:', event);
});
2

Start the download

await client.sftpDownload(
  '/remote/large-file.zip',
  RNFS.DocumentDirectoryPath
);

Upload Progress

1

Register the progress handler

Set up the UploadProgress event listener before starting the upload:
client.on('UploadProgress', (event) => {
  console.log('Upload progress:', event);
});
2

Start the upload

await client.sftpUpload(
  `${RNFS.DocumentDirectoryPath}/large-file.zip`,
  '/remote/uploads/'
);

Complete Progress Example

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>
  );
};
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);
  }
}
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

1

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
);
2

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);
3

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));
    }
  }
};
4

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