> ## Documentation Index
> Fetch the complete documentation index at: https://dylankenneally-react-native-ssh-sftp-96.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# File Transfers

> Upload and download files with progress tracking and cancellation support.

export const EditThisPage = ({filePath}) => {
  const REPO_EDIT_BASE_URL = 'https://github.com/dylankenneally/react-native-ssh-sftp/edit/master';
  return <div className="mt-10 rounded-2xl border border-zinc-950/10 bg-zinc-50 px-4 py-3 dark:border-white/10 dark:bg-white/5">
      <p className="m-0 text-sm text-zinc-600 dark:text-zinc-300">
        See something to improve?{' '}
        <a href={`${REPO_EDIT_BASE_URL}/${filePath}`}>Edit this page on GitHub</a>{' '}
        to suggest a change.
      </p>
    </div>;
};

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:

```javascript theme={null}
const downloadedPath = await client.sftpDownload(
  '/remote/path/to/file.txt',
  '/local/path/to/save/'
);

console.log('File downloaded to:', downloadedPath);
```

### Basic Download Example

<Tabs>
  <Tab title="Async/Await">
    ```javascript theme={null}
    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);
      }
    };
    ```
  </Tab>

  <Tab title="Callback">
    ```javascript theme={null}
    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();
        }
      );
    });
    ```
  </Tab>
</Tabs>

## Uploading Files

Upload files from your local device to the remote server:

```javascript theme={null}
await client.sftpUpload(
  '/local/path/to/file.txt',
  '/remote/path/to/destination/'
);

console.log('File uploaded successfully');
```

### Basic Upload Example

```javascript theme={null}
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

<Steps>
  <Step title="Register the progress handler">
    Set up the `DownloadProgress` event listener **before** starting the download:

    ```javascript theme={null}
    client.on('DownloadProgress', (event) => {
      console.log('Download progress:', event);
    });
    ```
  </Step>

  <Step title="Start the download">
    ```javascript theme={null}
    await client.sftpDownload(
      '/remote/large-file.zip',
      RNFS.DocumentDirectoryPath
    );
    ```
  </Step>
</Steps>

### Upload Progress

<Steps>
  <Step title="Register the progress handler">
    Set up the `UploadProgress` event listener **before** starting the upload:

    ```javascript theme={null}
    client.on('UploadProgress', (event) => {
      console.log('Upload progress:', event);
    });
    ```
  </Step>

  <Step title="Start the upload">
    ```javascript theme={null}
    await client.sftpUpload(
      `${RNFS.DocumentDirectoryPath}/large-file.zip`,
      '/remote/uploads/'
    );
    ```
  </Step>
</Steps>

### Complete Progress Example

<Tabs>
  <Tab title="Download with UI">
    ```javascript theme={null}
    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>
      );
    };
    ```
  </Tab>

  <Tab title="Upload with UI">
    ```javascript theme={null}
    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>
      );
    };
    ```
  </Tab>

  <Tab title="Both with Stats">
    ```javascript theme={null}
    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>
    ```
  </Tab>
</Tabs>

<Note>
  Progress event handlers must be registered **before** initiating the transfer. The SFTP connection automatically registers progress listeners when established.
</Note>

## Canceling Transfers

You can cancel ongoing uploads and downloads at any time.

### Cancel Download

```javascript theme={null}
client.sftpCancelDownload();
```

### Cancel Upload

```javascript theme={null}
client.sftpCancelUpload();
```

### Complete Cancellation Example

```javascript theme={null}
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>
  );
};
```

<Warning>
  * 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
</Warning>

## Working with Different File Types

### Text Files

```javascript theme={null}
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.)

```javascript theme={null}
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

<Note>
  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
</Note>

```javascript theme={null}
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:

<Accordion title="Sequential Transfers">
  ```javascript theme={null}
  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);
    }
  }
  ```
</Accordion>

<Accordion title="Parallel Transfers (Multiple Clients)">
  ```javascript theme={null}
  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);
  ```
</Accordion>

## Best Practices

<Steps>
  <Step title="Always use absolute paths">
    ```javascript theme={null}
    // Good
    await client.sftpDownload(
      '/home/user/file.txt',
      RNFS.DocumentDirectoryPath
    );

    // Avoid relative paths
    await client.sftpDownload(
      'file.txt',  // Where is this?
      RNFS.DocumentDirectoryPath
    );
    ```
  </Step>

  <Step title="Check available storage space">
    ```javascript theme={null}
    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);
    ```
  </Step>

  <Step title="Handle network interruptions">
    ```javascript theme={null}
    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));
        }
      }
    };
    ```
  </Step>

  <Step title="Clean up partial transfers">
    ```javascript theme={null}
    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;
    }
    ```
  </Step>
</Steps>

## Next Steps

* Learn about [SFTP operations](/guides/sftp-operations) for directory management
* Explore [SSH command execution](/guides/ssh-commands) for remote operations
* Check [platform-specific considerations](/guides/platform-specific) for iOS and Android differences

<EditThisPage filePath="docs/guides/file-transfers.mdx" />
