Die beste Alternative zu cordova-plugin-file-transfer für eine Capacitor-App hängt von einer Frage ab: Muss der Transfer weiterlaufen, wenn die App in den Hintergrund geht? Für normale Uploads und Downloads mit Fortschrittsanzeige nutze das offizielle @capacitor/file-transfer Plugin. Für große Dateien, instabile Netzwerke oder Transfers, die auch beim App-Wechsel weiterlaufen müssen, verwende @capgo/capacitor-uploader und @capgo/capacitor-downloader, die auf nativen Background-Transfer-APIs laufen. Dieser Guide vergleicht sie und ordnet jeden FileTransfer Aufruf zu.
Ist cordova-plugin-file-transfer immer noch deprecated?
Die Historie ist verwirrend, deshalb hier die Fakten:
- Im Jahr 2017 kündigte das Apache Cordova-Team an, das Plugin als deprecated zu markieren, und verwies Entwickler auf
XMLHttpRequestLevel 2. - Das Plugin wurde auf npm nie als deprecated markiert. Version 2.0.0 erschien im September 2023, 2.0.1 im August 2026. Das GitHub-Repository ist aktiv.
Es ist also nicht tot. Aber in einer Capacitor-App ist es immer noch ein Cordova-Plugin, das:
- abhängt von
cordova-plugin-fileund dessencdvfile://undfile://Pfadmodell, - passt nicht zu Capacitor
FilesystemVerzeichnisse und URI-Konvertierungen, - unterstützt keine Hintergrund-Transfers,
- nutzt Callback-basierte APIs.
Wenn Sie von Cordova migrieren, ersetzt die Nutzung nativer Capacitor-Plugins einen der letzten Gründe, cordova-plugin-file.
Warum „einfach fetch nutzen“ bei großen Dateien scheitert
Der von Apache empfohlene Ersatz XHR funktioniert für kleine JSON-Daten und Bilder. Bei echten Datei-Transfers in einer Hybrid-App scheitert er:
- Speicher.
fetchund XHR liefern Ihnen einBloboderArrayBufferin der WebView. Ein 500-MB-Video bedeutet 500 MB WebView-Speicher, plus eine Kopie für die Übertragung über die Bridge. Auf Mittelklasse-Android-Geräten crasht dadurch der Renderer. - Suspension. Wechselt der Nutzer die App, suspendiert iOS die WebView. Laufende Requests stocken oder schlagen fehl.
- Keine OS-Integration. Keine System-Download-Benachrichtigung, keine Option nur über WLAN, kein Fortsetzen nach Verbindungsabbruch.
Native Plugins streamen direkt zwischen Disk und Netzwerk und halten JavaScript aus dem Datenpfad heraus.
Die Optionen im Vergleich
| Benötigt | cordova-plugin-file-transfer | @capacitor/file-transfer | @capgo/capacitor-uploader | @capgo/capacitor-downloader |
|---|---|---|---|---|
| Auf Disk herunterladen | Ja | Ja | Nein | Ja |
| Vom Datenträger hochladen | Ja | Ja | Ja | Nein |
| Progress-Events | Ja | Ja (progress listener) |
Ja (events) |
Ja (downloadProgress) |
| Multipart-Formdaten | Ja | Ja (Standard) | Ja | k. A. |
| Rohes binäres PUT (presigned URLs) | Ja | Ja (gesetzt Content-Type) |
Ja (uploadType: 'binary') |
k. A. |
| Mehrere Dateien in einer Anfrage | Nein | Nein | Ja (files) |
k. A. |
| Abbrechen | abort() |
Nein | removeUpload |
stop |
| Pausieren / Fortsetzen | Nein | Nein | Nein | pause, resume |
| Wiederholungen | Nein | Nein | maxRetries |
Nein |
| Läuft im Hintergrund weiter | Nein | Während die App aktiv ist | Ja | Ja |
| Nur über WLAN | Nein | Nein | Nein | network: 'wifi-only' |
| HTTP-Fehlerdetails | http_status, body |
httpStatus, body |
Status code in Events | Fehlerstring in Events |
| Web-Implementierung | Nein | Ja | Ja (fetch-basiert) | Nein |
Die meisten Apps nutzen am Ende @capacitor/file-transfer für kleine, für Nutzer sichtbare Transfers und die Capgo Plugins für Media-Upload-Queues und Offline-Content-Downloads.
Option 1: @capacitor/file-transfer
bun add @capacitor/file-transfer @capacitor/filesystem
bunx cap sync
Das Plugin benötigt einen vollständigen nativen Pfad. Holen Sie ihn von @capacitor/filesystem.
Download
import { FileTransfer } from '@capacitor/file-transfer';
import { Filesystem, Directory } from '@capacitor/filesystem';
export async function downloadReport(url: string) {
const { uri } = await Filesystem.getUri({
directory: Directory.Data,
path: 'reports/latest.pdf',
});
const progress = await FileTransfer.addListener('progress', (p) => {
if (p.type === 'download' && p.lengthComputable) {
console.log(`Downloaded ${Math.round((p.bytes / p.contentLength) * 100)}%`);
}
});
try {
const result = await FileTransfer.downloadFile({
url,
path: uri,
progress: true,
headers: { Authorization: 'Bearer <token>' },
});
return result.path;
} catch (error: any) {
if (error.code === 'OS-PLUG-FLTR-0010') {
console.error('HTTP error', error.data?.httpStatus, error.data?.body);
}
throw error;
} finally {
await progress.remove();
}
}
Upload
export async function uploadAvatar(path: string) {
const result = await FileTransfer.uploadFile({
url: 'https://api.example.com/v1/avatar',
path,
fileKey: 'avatar',
mimeType: 'image/jpeg',
headers: { Authorization: 'Bearer <token>' },
progress: true,
});
console.log(result.responseCode, result.response);
}
Uploads sind standardmäßig multipart. Für einen Raw-Stream, z. B. zu einer presigned S3-URL, setze method: 'PUT' und einen expliziten Content-Type Header wie zum Beispiel application/octet-stream.
Fehlercodes folgen dem OS-PLUG-FLTR-XXXX Format. Die wichtigsten sind: 0007 Datei nicht gefunden, 0008 Verbindung fehlgeschlagen, 0010 HTTP-Fehlerstatus mit httpStatus und body in einem error.data.
Option 2: Background-Uploads mit @capgo/capacitor-uploader
bun add @capgo/capacitor-uploader
bunx cap sync
import { Uploader } from '@capgo/capacitor-uploader';
await Uploader.addListener('events', async (event) => {
switch (event.name) {
case 'uploading':
console.log(event.id, `${event.payload.percent}%`);
break;
case 'completed':
console.log(event.id, 'done', event.payload.statusCode);
break;
case 'failed':
console.error(event.id, event.payload.error);
break;
}
// Completed and failed events are cached natively until acknowledged
if (event.eventId) {
await Uploader.acknowledgeEvent({ eventId: event.eventId });
}
});
const { id } = await Uploader.startUpload({
filePath: 'file:///path/to/video.mp4',
serverUrl: 'https://api.example.com/v1/videos',
method: 'POST',
uploadType: 'multipart',
fileField: 'video',
parameters: { albumId: '7' },
headers: { Authorization: 'Bearer <token>' },
maxRetries: 3,
notificationTitle: 'Uploading video', // Android only
});
Für presigned URLs verwenden Sie method: 'PUT', standardmäßig mit binärem Body:
await Uploader.startUpload({
filePath,
serverUrl: presignedUrl,
method: 'PUT',
headers: { 'Content-Type': 'video/mp4' },
mimeType: 'video/mp4',
});
Mehrere Dateien in einer Multipart-Anfrage:
await Uploader.startUpload({
serverUrl: 'https://api.example.com/v1/photos',
method: 'POST',
uploadType: 'multipart',
files: [
{ filePath: photo1, fieldName: 'images[]', mimeType: 'image/jpeg' },
{ filePath: photo2, fieldName: 'images[]', mimeType: 'image/jpeg' },
],
headers: { Authorization: 'Bearer <token>' },
});
Abbrechen mit Uploader.removeUpload({ id }).
Warum Events bestätigen? Abgeschlossene und fehlgeschlagene Events werden in einem persistenten nativen Cache gespeichert, sodass sie erneut zugestellt werden können, wenn die App geschlossen oder in den Hintergrund versetzt wurde, bevor sie die Events verarbeitet hat. Das Bestätigen entfernt das Event, damit es bei der nächsten Initialisierung des Plugins nicht erneut gesendet wird.
iOS-Setup. Der Uploader nutzt eine Hintergrund- URLSession. Viele Apps benötigen kein extra Setup. Wenn Uploads weiterlaufen müssen, nachdem die App suspendiert wurde, füge fetch zu UIBackgroundModes. Nicht hinzufügen processing es sei denn, ein anderes Feature nutzt BGTaskScheduler, da App Store Connect Builds ablehnt, die es ohne Angabe deklarieren BGTaskSchedulerPermittedIdentifiers.
Option 3: Hintergrund-Downloads mit @capgo/capacitor-downloader
bun add @capgo/capacitor-downloader
bunx cap sync
import { CapacitorDownloader } from '@capgo/capacitor-downloader';
await CapacitorDownloader.addListener('downloadProgress', ({ id, progress }) => {
console.log(id, `${progress}%`);
});
await CapacitorDownloader.addListener('downloadCompleted', ({ id }) => {
console.log(id, 'completed');
});
await CapacitorDownloader.addListener('downloadFailed', ({ id, error }) => {
console.error(id, error);
});
await CapacitorDownloader.download({
id: 'course-42',
url: 'https://cdn.example.com/courses/42.zip',
destination: 'courses/42.zip',
headers: { Authorization: 'Bearer <token>' },
network: 'wifi-only',
priority: 'normal',
notification: 'progress', // Android: remove the system notification when done
});
// Later
await CapacitorDownloader.pause({ id: 'course-42' });
await CapacitorDownloader.resume({ id: 'course-42' });
const status = await CapacitorDownloader.checkStatus({ id: 'course-42' });
console.log(status.state, status.progress);
Ein relativer destination entspricht auf iOS dem Documents-Verzeichnis der App und auf Android dem app-spezifischen externen Files-Verzeichnis. Du kannst auch einen absoluten Pfad oder eine file:// URL hinzu. Nutze getFileInfo({ path }) um Größe und MIME-Typ nach dem Download zu lesen, und stop({ id }) um abgebrochene Daten zu stornieren und zu löschen.
Migration von cordova-plugin-file-transfer
Download
// Before
const ft = new FileTransfer();
ft.onprogress = (e) => e.lengthComputable && setProgress(e.loaded / e.total);
ft.download(
encodeURI(url),
cordova.file.dataDirectory + 'report.pdf',
(entry) => console.log(entry.toURL()),
(err) => console.error(err.code, err.http_status),
false,
{ headers: { Authorization: 'Bearer <token>' } },
);
// After
const { uri } = await Filesystem.getUri({ directory: Directory.Data, path: 'report.pdf' });
await FileTransfer.downloadFile({
url,
path: uri,
progress: true,
headers: { Authorization: 'Bearer <token>' },
});
Upload
// Before
const options = new FileUploadOptions();
options.fileKey = 'file';
options.fileName = 'photo.jpg';
options.mimeType = 'image/jpeg';
options.params = { albumId: '7' };
options.headers = { Authorization: 'Bearer <token>' };
new FileTransfer().upload(fileUrl, encodeURI(serverUrl), onSuccess, onError, options);
// After, foreground
await FileTransfer.uploadFile({
url: serverUrl,
path: fileUrl,
fileKey: 'file',
mimeType: 'image/jpeg',
params: { albumId: '7' },
headers: { Authorization: 'Bearer <token>' },
});
// After, background
await Uploader.startUpload({
filePath: fileUrl,
serverUrl,
uploadType: 'multipart',
fileField: 'file',
parameters: { albumId: '7' },
headers: { Authorization: 'Bearer <token>' },
});
Beachte, dass params in einem @capacitor/file-transfer sind URL-Query-Parameter. Wenn Ihr Server Formularfelder erwartet, verwenden Sie den Capgo-Uploader parameters, die als multipart-Formularfelder gesendet werden.
Mapping-Tabelle
| cordova-plugin-file-transfer | @capacitor/file-transfer | Capgo Plugins |
|---|---|---|
download(source, target, ...) |
downloadFile({ url, path }) |
CapacitorDownloader.download({ id, url, destination }) |
upload(file, server, ...) |
uploadFile({ url, path }) |
Uploader.startUpload({ filePath, serverUrl }) |
onprogress |
addListener('progress') |
addListener('events') / addListener('downloadProgress') |
abort() |
nicht verfügbar | removeUpload({ id }) / stop({ id }) |
options.fileKey |
fileKey |
fileField oder files[].fieldName |
options.params |
params (Query-String) |
parameters (Formularfelder) |
options.httpMethod |
method |
method (POST oder PUT) |
options.chunkedMode |
chunkedMode |
Nativ verarbeitet |
cordova.file.dataDirectory |
Filesystem.getUri({ directory: Directory.Data }) |
Relativer Pfad oder file:// URL |
FileTransferError.http_status |
error.data.httpStatus |
event.payload.statusCode |
Pfade: der Teil, der Migrationen kaputt macht
Cordova code übergibt cdvfile:// oder cordova.file.* Pfade überall. In Capacitor:
- Native Pfade von
Filesystem.getUri()oder vom Plugin, das die Datei erstellt hat (Kamera, Dateiauswahl, Recorder). - Um ein heruntergeladenes Bild im WebView anzuzeigen, konvertiere den nativen Pfad mit
Capacitor.convertFileSrc(path). - Capgo hat ein File-Plugin und einen File Picker falls
@capacitor/filesystemdeinen Anwendungsfall nicht abdeckt.
Fehlerbehebung
Der Upload funktioniert auf Android, schlägt aber auf iOS mit einem 'File not found'-Fehler fehl. Du hast eine capacitor:// WebView-URL statt eines nativen file:// Pfads übergeben. Nutze den Pfad von Filesystem.getUri().
Der Server lehnt den Upload mit 415 ab. Mismatch zwischen Multipart und Raw Body. Nutze für Presigned URLs PUT mit einem expliziten Content-Type.
Background-Uploads brechen auf iOS ab. Der User hat die App zwangsbeendet, wodurch Background-Sessions abgebrochen werden, oder fetch der Background-Modus fehlt, wenn man ihn braucht.
Android zeigt eine hängende Download-Benachrichtigung. Setzen Sie notification: 'progress' oder 'hidden' beim Downloader.
Memory-Crash bei großen Downloads. Du nutzt immer noch fetch oder liest die Datei als Base64 über die Bridge. Nutze stattdessen einen nativen Download auf die Disk.
Migration ausliefern
Der Austausch nativer Plugins erfordert einen neuen Store-Build, was Capgo Build läuft in der Cloud für iOS und Android. Danach lassen sich die JavaScript-Logik für Uploads und Downloads beheben und verbessern mit Capgo Live Updates. Für den Rest des Wechsels von Cordova zu Capacitor siehe unsere Migrationsleitfaden, und für andere kostenpflichtige oder veraltete native SDKs die Ionic Enterprise Plugin-Alternativen.