---
title: Announcing the Capacitor File Transfer Plugin
description: Our new Capacitor File Transfer plugin runs background uploads and downloads with pause and resume, persisted tasks, retries, and progress events.
date:
  created: 2026-08-27
  updated: 2026-08-27
authors:
  - robingenz
categories:
  - Announcements
  - Capacitor
  - SDKs
links:
  - Capacitor File Transfer: sdks/capacitor/file-transfer.md
faq: true
---

# Announcing the Capacitor File Transfer Plugin

The official [`@capacitor/file-transfer`](https://capacitorjs.com/docs/apis/file-transfer){:target="_blank"} plugin downloads and uploads files as long as your app stays in the foreground. Everything around that simple case is where real apps struggle: the user switches apps mid-download, the operating system kills the process, a large transfer should wait for Wi-Fi, or a download list has to survive a restart. Today we're announcing the [Capacitor File Transfer plugin](../../sdks/capacitor/file-transfer.md) by Capawesome, which models exactly that lifecycle: task-based background uploads and downloads with pause and resume, automatic retries, network constraints, and a persisted task store. It's available today to all Capawesome [Insiders](../../insiders/index.md).

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="https://capawesome.io/" target="_blank">
    <img alt="Build and deploy your Capacitor app with Capawesome Cloud" src="https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
  </a>
</div>

**Key takeaways:**

- `@capawesome-team/capacitor-file-transfer` runs task-based uploads and downloads that continue in the background: a background `URLSession` on iOS and a `dataSync` foreground service with its own OkHttp engine on Android.
- Downloads can be paused and resumed, even after the process was killed, via resume data on iOS and HTTP `Range` requests on Android.
- Transfers are persisted: `getTransfers()` returns them after an app restart, so a transfer manager UI can be rebuilt on launch.
- `network: 'unmetered'` restricts a transfer to Wi-Fi, and failed transfers retry automatically with backoff.
- Uploads support `multipart/form-data` and binary bodies for S3 presigned URLs.
- Requires Capacitor 8 or later, no web implementation, part of the Capawesome Insiders subscription.

## Why Another Capacitor File Transfer Plugin?

The official plugin is a fine choice for a foreground fetch: give it a URL and a path, get progress events, done. But it has no equivalent for pausing, resuming, or canceling a transfer, no way to list transfers after a restart, and no answer to what happens when the app leaves the foreground. Those gaps are the actual engineering problem in file transfers, because large files and short attention spans go together: the bigger the download, the more likely the user is somewhere else when it finishes.

So we built a plugin where the transfer is a first-class object. Starting one returns an identifier immediately, the work happens in native code that outlives your web view, and the state is persisted where your JavaScript can query it later. Instead of promising more than the platforms allow, the plugin documents its exact behavior in every app state, including force-quit.

The result shows up in very ordinary features: offline video or course packs that finish downloading while the user does something else, media uploads that survive a subway ride, a Wi-Fi-only setting that actually works, and a download manager screen that still knows its state after a restart. Transfers are also rarely the whole pipeline, so the plugin works hand in hand with the [Capacitor File Compressor plugin](../../sdks/capacitor/file-compressor.md) for shrinking files before an upload and the [Capacitor File Opener plugin](../../sdks/capacitor/file-opener.md) for opening finished downloads in another app.

## Installation

To install the Capacitor File Transfer plugin, please refer to the [Installation](../../sdks/capacitor/file-transfer.md/#installation) section in the plugin documentation. It's published to the Capawesome npm registry, so installation requires the license key that comes with a [Capawesome Insiders](../../insiders/index.md) subscription.

The platform setup is deliberately small. On Android, the plugin declares all required permissions and the `dataSync` foreground service in its own manifest, so no manifest changes are needed. One runtime detail is worth planning for: on Android 13 and later, the per-transfer progress notification only appears if the user granted the `POST_NOTIFICATIONS` permission, which you can request with [`requestPermissions()`](../../sdks/capacitor/file-transfer.md#requestpermissions). Transfers run either way; only the notification stays hidden.

On iOS, you add a short `AppDelegate` extension that forwards the background `URLSession` completion handler to the plugin. This hook lets iOS wake your app when a transfer finishes while it isn't running; the exact snippet is in the documentation.

## Usage

Here's what working with transfers looks like, from starting a download to restoring the state after a restart.

### Start a download

[`startDownload(...)`](../../sdks/capacitor/file-transfer.md#startdownload) resolves immediately with the identifier of the transfer, while the download itself runs natively in the background. Options cover authentication headers, a Wi-Fi-only constraint, automatic retries, and an Android progress notification:

```typescript
import { FileTransfer } from '@capawesome-team/capacitor-file-transfer';

const startDownload = async () => {
  const { id } = await FileTransfer.startDownload({
    url: 'https://example.com/file.zip',
    path: '/path/to/destination/file.zip',
    headers: {
      Authorization: 'Bearer <token>',
    },
    network: 'unmetered',
    maxRetries: 3,
    androidNotification: {
      title: 'Downloading file',
      text: 'The file is being downloaded.',
    },
  });
  return id;
};
```

The `network: 'unmetered'` option keeps a large download off the user's mobile data plan, and `maxRetries` handles flaky connections with backoff instead of failing on the first dropped packet. The `androidNotification` option controls what the user sees while the service runs: Android always shows a notification for a running foreground service, and setting `progress: true` opts into a live progress bar for the transfer.

### Upload to your backend or to S3

[`startUpload(...)`](../../sdks/capacitor/file-transfer.md#startupload) sends `multipart/form-data` by default and switches to a raw binary body for S3 presigned URLs:

```typescript
import { FileTransfer } from '@capawesome-team/capacitor-file-transfer';

const uploadToBackend = async () => {
  const { id } = await FileTransfer.startUpload({
    url: 'https://example.com/upload',
    path: '/path/to/source/file.jpg',
    uploadType: 'multipart',
    fileField: 'file',
    mimeType: 'image/jpeg',
    formFields: {
      albumId: '42',
    },
  });
  return id;
};

const uploadToPresignedUrl = async () => {
  const { id } = await FileTransfer.startUpload({
    url: 'https://example.com/presigned-url',
    path: '/path/to/source/file.jpg',
    method: 'PUT',
    uploadType: 'binary',
    mimeType: 'image/jpeg',
  });
  return id;
};
```

In both cases, the file streams straight from the file system into the request. No base64 detour, no blob in JavaScript memory.

### Track progress and completion

Transfers report their state through three events. Register the listeners early in your app startup:

```typescript
import { FileTransfer } from '@capawesome-team/capacitor-file-transfer';

const addTransferListeners = async () => {
  await FileTransfer.addListener('transferProgress', event => {
    console.log(`Transfer ${event.id}: ${event.bytes}/${event.totalBytes}`);
  });
  await FileTransfer.addListener('transferCompleted', event => {
    console.log(`Transfer ${event.id} completed: `, event.path);
  });
  await FileTransfer.addListener('transferFailed', event => {
    console.error(`Transfer ${event.id} failed: `, event.errorCode, event.message);
  });
};
```

One detail matters more than it looks: `transferCompleted` and `transferFailed` events that occur while no listener is registered are retained and delivered as soon as a listener is added. A download that finishes while your app is in the background still reaches your code on the next launch.

### Pause, resume, and restore

Downloads can be paused with [`pauseTransferById(...)`](../../sdks/capacitor/file-transfer.md#pausetransferbyid) and continued with [`resumeTransferById(...)`](../../sdks/capacitor/file-transfer.md#resumetransferbyid), and because paused downloads survive process death, resuming also works after an app restart. [`getTransfers()`](../../sdks/capacitor/file-transfer.md#gettransfers) returns all known transfers, which is how a download manager UI restores its list on launch:

```typescript
import { FileTransfer } from '@capawesome-team/capacitor-file-transfer';

const restoreTransferList = async () => {
  const { transfers } = await FileTransfer.getTransfers();
  return transfers.filter(transfer => transfer.state === 'running' || transfer.state === 'paused');
};
```

Uploads cannot be paused in this version. Plain HTTP uploads have no standard resume mechanism, so the plugin rejects instead of faking a pause with a suspend that would restart from zero anyway.

### Cancel a transfer

Canceling with [`cancelTransferById(...)`](../../sdks/capacitor/file-transfer.md#canceltransferbyid) stops a running or paused transfer and deletes any partially transferred data, so a canceled transfer cannot be resumed. Reach for pause when the user might come back and for cancel when they won't.

## What Happens When the App Goes to the Background?

The transfer keeps running, and the exact behavior per app state is documented rather than left to experimentation:

| App state | Android | iOS |
| --- | --- | --- |
| Foreground | Runs. | Runs. |
| Backgrounded | Runs in the `dataSync` foreground service. | Runs in the background `URLSession`. |
| Killed by the OS | Interrupted; restored as `failed`, downloads resumable. | Continued by the OS and delivered on relaunch. |
| Force-quit by the user | Interrupted; restored as `failed`, downloads resumable. | Canceled by the OS (documented iOS behavior). |

Resuming an interrupted download requires the server to support the HTTP `Range` header. And on iOS, a force-quit canceling background work is an operating system rule that no plugin can bypass; the difference is that this one tells you.

## Migrating from @capacitor/file-transfer

If you already use the official plugin, the switch is mostly a rename plus a change of model: transfers become asynchronous tasks, so the start methods resolve immediately with an `id`, and completion arrives through events instead of the returned promise.

| `@capacitor/file-transfer` | `@capawesome-team/capacitor-file-transfer` |
| --- | --- |
| `downloadFile({ url, path })` | `startDownload({ url, path })`, resolves with `{ id }` |
| `uploadFile({ url, path })` | `startUpload({ url, path })`, resolves with `{ id }` |
| `addListener('progress', ...)` | `addListener('transferProgress', ...)` |
| No equivalent | `transferCompleted` and `transferFailed` events |
| No equivalent | `pauseTransferById`, `resumeTransferById`, `cancelTransferById` |
| No equivalent | `getTransferById`, `getTransfers` |

The [migration section](../../sdks/capacitor/file-transfer.md#migration-from-capacitorfile-transfer) in the plugin documentation keeps this mapping up to date.

## FAQ

### What's the difference between @capacitor/file-transfer and the Capawesome File Transfer plugin?

The official `@capacitor/file-transfer` plugin covers foreground downloads and uploads with progress events. The [Capacitor File Transfer plugin](../../sdks/capacitor/file-transfer.md) by Capawesome adds background continuation on both platforms, pause and resume that survives process death, a persisted task store, automatic retries, network constraints, and completion events that are retained across launches. The [migration section](../../sdks/capacitor/file-transfer.md#migration-from-capacitorfile-transfer) in the documentation maps both APIs side by side.

### Do transfers continue when the app is in the background?

Yes. On Android, transfers run in a `dataSync` foreground service; on iOS, in a background `URLSession`. On iOS, downloads even continue when the operating system kills the app for memory, and the result is delivered on the next launch.

### Can I pause an upload?

No, only downloads can be paused and resumed. HTTP uploads have no standard resume mechanism, so `pauseTransferById(...)` rejects for uploads instead of pretending.

### Does the plugin work on the web?

No. Transfers require native background APIs, so `startDownload(...)` and `startUpload(...)` reject as unavailable on the web.

### Can I use the plugin with Ionic, React, Vue, or Angular?

Yes. The plugin is framework-agnostic and works in any Capacitor app, including Ionic with Angular, React, or Vue, as well as plain JavaScript projects.

## Availability

The Capacitor File Transfer plugin is available today to all Capawesome [Insiders](../../insiders/index.md) and requires Capacitor 8 or later. The same subscription covers every other Insiders plugin, so there's no separate license to buy.

New plugins and notable releases are announced in the Capawesome newsletter first.

[Subscribe to the Capawesome Newsletter](https://capawesome.io/newsletter/){ .md-button .md-button--primary }

## Conclusion

A file transfer is easy to start and hard to finish: the interesting work is what happens across backgrounding, process death, network changes, and restarts. The Capacitor File Transfer plugin puts that work into native code with a task-based API and tells you honestly what each platform does in each state.

**Further reading:**

- [An Alternative to cordova-plugin-file-transfer](./alternative-to-cordova-plugin-file-transfer.md) — if you're coming from the Cordova plugin or the "just use XHR" advice
- [The Capacitor file handling guide](./capacitor-file-handling-guide.md) — the bigger picture of files in Capacitor apps
- [API Reference](../../sdks/capacitor/file-transfer.md#api) — every method, option, event, and error code

**Missing a feature?** [Create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose){:target="_blank"} in our GitHub repository.

If you have any questions, join us on the [Capawesome Discord server](https://discord.gg/VCXxSVjefW){:target="_blank"}. To stay updated on the latest news, subscribe to the [Capawesome newsletter](https://capawesome.io/newsletter/){:target="_blank"}.
