DEV Community

Robin for Capawesome

Posted on Originally published at capawesome.io

Announcing the Capacitor File Transfer Plugin

If you've ever shipped a download feature in a Capacitor app, you know the failure modes: the user switches apps halfway through a 500 MB file, the operating system kills the process, or a big transfer quietly eats someone's mobile data plan. The official @capacitor/file-transfer plugin covers foreground transfers; we just released the Capacitor File Transfer plugin to handle everything around them.

Transfers as Tasks

The core idea: a transfer is a first-class, persisted object. Starting one returns an identifier immediately, the work runs in native code that outlives your web view (a background URLSession on iOS, a dataSync foreground service on Android), and the state can be queried later — even after an app restart.

Start a Download

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 ',
    },
    network: 'unmetered',
    maxRetries: 3,
    androidNotification: {
      title: 'Downloading file',
      text: 'The file is being downloaded.',
    },
  });
  return id;
};
Enter fullscreen mode Exit fullscreen mode

network: 'unmetered' keeps the download off mobile data, and maxRetries retries with backoff on network errors instead of failing on the first dropped packet.

Uploads, Including S3 Presigned URLs

Uploads send multipart/form-data by default and switch to a raw binary body for presigned URLs:

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

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;
};
Enter fullscreen mode Exit fullscreen mode

The file streams straight from the file system into the request — no base64 detour, no blob in JavaScript memory.

Events That Survive Backgrounding

Transfers report their state through transferProgress, transferCompleted, and transferFailed events. The important detail: completed and failed events that occur while no listener is registered are retained and delivered as soon as a listener is added, so a download that finishes while your app is in the background still reaches your code on the next launch.

Pause, Resume, Restore

Downloads can be paused and resumed at any time — even after the process was killed, via resume data on iOS and HTTP Range requests on Android. And because transfers are persisted, getTransfers() rebuilds a download manager UI after a restart:

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');
};
Enter fullscreen mode Exit fullscreen mode

Uploads cannot be paused: plain HTTP uploads have no standard resume mechanism, so the plugin rejects instead of faking a pause that would restart from zero anyway.

What Happens in Each App State

Instead of leaving it to experimentation, the behavior is documented per platform:

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.

Migrating from @capacitor/file-transfer

The switch is mostly a rename plus a change of model — transfers become asynchronous tasks:

@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

Availability

The plugin is part of the Capawesome Insiders subscription and requires Capacitor 8 or later. The full announcement is on our blog: Announcing the Capacitor File Transfer Plugin, and the plugin documentation covers the complete API.

Questions or feedback? Drop a comment — happy to answer.

Top comments (0)