La migliore alternativa a cordova-plugin-file-transfer per un'app Capacitor dipende da una domanda: il trasferimento deve sopravvivere al passaggio dell'app in background? Per upload e download normali con avanzamento, usa il plugin ufficiale @capacitor/file-transfer plugin. Per file grandi, reti instabili o trasferimenti che devono continuare quando l'utente cambia app, usa @capgo/capacitor-uploader e @capgo/capacitor-downloader, che usano le API native di trasferimento in background. Questa guida le confronta e mappa ogni FileTransfer chiamata.
cordova-plugin-file-transfer è ancora deprecato?
La storia è confusa, quindi ecco i fatti:
- Nel 2017 il team di Apache Cordova ha annunciato l'intenzione di deprecare il plugin, indirizzando gli sviluppatori verso
XMLHttpRequestLivello 2. - Il plugin non è mai stato contrassegnato come deprecato su npm. La versione 2.0.0 è uscita a settembre 2023 e la 2.0.1 ad agosto 2026. Il repository GitHub è attivo.
Quindi non è morto. Ma in un'app Capacitor è ancora un plugin Cordova che:
- dipende da
cordova-plugin-filee il suocdvfile://efile://modello path, - non si adatta a Capacitor
Filesystemconversioni di directory e URI, - non supporta i trasferimenti in background,
- usa API in stile callback.
Se stai migrando da Cordova, sostituirlo con i plugin nativi di Capacitor elimina una delle ultime ragioni per mantenere cordova-plugin-file.
Perché “usare solo fetch” fallisce sui file grandi
La soluzione alternativa suggerita da Apache, XHR, funziona per piccoli JSON e immagini. Fallisce per il trasferimento reale di file in un'app ibrida:
- Memoria.
fetche XHR ti danno unBloboppureArrayBuffernel WebView. Un video da 500 MB significa 500 MB di memoria nel WebView, poi una copia per scriverlo tramite il bridge. Su dispositivi Android di fascia media, questo fa crashare il renderer. - Sospensione. Quando l'utente cambia app, iOS sospende il WebView. Le richieste in corso si bloccano o falliscono.
- Nessuna integrazione con il sistema operativo. Nessuna notifica di download di sistema, nessuna opzione solo Wi-Fi, nessuna ripresa dopo una connessione interrotta.
I plugin nativi fanno streaming direttamente tra disco e rete, tenendo JavaScript fuori dal percorso dei dati.
Confronto tra le opzioni
| Ti servono | cordova-plugin-file-transfer | @capacitor/file-transfer | @capgo/capacitor-uploader | @capgo/capacitor-downloader |
|---|---|---|---|---|
| Download su disco | Sì | Sì | No | Sì |
| Upload da disco | Sì | Sì | Sì | No |
| Eventi di avanzamento | Sì | Sì (progress listener) |
Sì (events) |
Sì (downloadProgress) |
| Dati del modulo multipart | Sì | Sì (predefinito) | Sì | n/d |
| PUT binario grezzo (URL presigned) | Sì | Sì (impostare Content-Type) |
Sì (uploadType: 'binary') |
n/d |
| Più file in una sola richiesta | No | No | Sì (files) |
n/d |
| Annulla | abort() |
No | removeUpload |
stop |
| Pausa / riprendi | No | No | No | pause, resume |
| Ritenti | No | No | maxRetries |
No |
| Continua in background | No | Mentre l'app è attiva | Sì | Sì |
| Solo Wi-Fi | No | No | No | network: 'wifi-only' |
| Dettagli errore HTTP | http_status, body |
httpStatus, body |
Stato code negli eventi | Stringa di errore negli eventi |
| Implementazione web | No | Sì | Sì (basato su fetch) | No |
La maggior parte delle app finisce per usare @capacitor/file-transfer per piccoli trasferimenti visibili all'utente e i plugin Capgo per le code di upload media e il download di contenuti offline.
Opzione 1: @capacitor/file-transfer
bun add @capacitor/file-transfer @capacitor/filesystem
bunx cap sync
Il plugin richiede un percorso nativo completo. Ottienilo da @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();
}
}
Carica
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);
}
Gli upload sono multipart di default. Per uno stream raw, ad esempio verso un URL S3 presigned, imposta method: 'PUT' e un esplicito Content-Type header come application/octet-stream.
I codici di errore seguono il OS-PLUG-FLTR-XXXX formato. I più utili: 0007 file non trovato, 0008 connessione non riuscita, 0010 stato di errore HTTP con httpStatus e body nella error.data.
Opzione 2: upload in background con @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
});
Per gli URL presigned, usa method: 'PUT', che di default usa un corpo binario:
await Uploader.startUpload({
filePath,
serverUrl: presignedUrl,
method: 'PUT',
headers: { 'Content-Type': 'video/mp4' },
mimeType: 'video/mp4',
});
Più file in una singola richiesta multipart:
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>' },
});
Annulla con Uploader.removeUpload({ id }).
Perché confermare gli eventi? Gli eventi completati e falliti vengono salvati in una cache nativa persistente, così possono essere ritrasmessi se l'app era stata chiusa o messa in background prima di gestirli. Confermare l'evento lo rimuove, evitando che venga ritrasmesso alla prossima inizializzazione del plugin.
iOS setup. L'uploader utilizza un background URLSession. Molte app non richiedono configurazioni aggiuntive. Se gli upload devono continuare dopo la sospensione dell'app, aggiungi fetch a UIBackgroundModes. Non aggiungere processing a meno che un'altra funzionalità non utilizzi BGTaskScheduler, perché App Store Connect rifiuta i build che lo dichiarano senza BGTaskSchedulerPermittedIdentifiers.
Opzione 3: download in background con @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);
Un parente destination viene risolto nella directory Documents dell'app su iOS e nella directory file esterna specifica dell'app su Android. Puoi anche passare un percorso assoluto o un file:// URL. Usa getFileInfo({ path }) per leggere dimensione e tipo MIME dopo il download, e stop({ id }) per annullare ed eliminare i dati parziali.
Migrazione da 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>' },
});
Carica
// 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>' },
});
Nota che params nella @capacitor/file-transfer sono parametri di query dell'URL. Se il tuo server si aspetta campi form, usa l'uploader di Capgo parameters, che vengono inviati come campi form multipart.
Tabella di corrispondenza
| cordova-plugin-file-transfer | @capacitor/file-transfer | Plugin Capgo |
|---|---|---|
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() |
non disponibile | removeUpload({ id }) / stop({ id }) |
options.fileKey |
fileKey |
fileField oppure files[].fieldName |
options.params |
params (query string) |
parameters (campi del form) |
options.httpMethod |
method |
method (POST oppure PUT) |
options.chunkedMode |
chunkedMode |
Gestito nativamente |
cordova.file.dataDirectory |
Filesystem.getUri({ directory: Directory.Data }) |
Percorso relativo o file:// URL |
FileTransferError.http_status |
error.data.httpStatus |
event.payload.statusCode |
Percorsi: la parte che rompe le migrazioni
Cordova code passa cdvfile:// oppure cordova.file.* ovunque. In Capacitor:
- Ottieni i percorsi nativi da
Filesystem.getUri()o dal plugin che ha creato il file (camera, file picker, recorder). - Per visualizzare un'immagine scaricata nella WebView, converti il percorso nativo con
Capacitor.convertFileSrc(path). - Capgo ha un plugin per file e a una selettore file se
@capacitor/filesystemnon copre il tuo caso.
Risoluzione problemi
L'upload funziona su Android ma fallisce su iOS con un errore file non trovato. Hai passato un capacitor:// URL della WebView invece di un file:// path nativo. Usa il path da Filesystem.getUri().
Il server rifiuta l'upload con errore 415. Mismatch tra multipart e raw body. Per gli URL presigned usa PUT with an explicit Content-Type.
Gli upload in background si interrompono su iOS. L'utente chiude forzatamente l'app, annullando le sessioni in background, oppure fetch la modalità background non è disponibile quando serve.
Su Android appare una notifica di download bloccata. Imposta notification: 'progress' oppure 'hidden' sul downloader.
Crash di memoria durante download di grandi dimensioni. Stai ancora usando fetch oppure leggendo il file come base64 tramite il bridge. Passa a un download nativo su disco.
Pubblica la migrazione
Sostituire i plugin nativi richiede una nuova build per lo store, che Build Capgo può essere eseguita nel cloud per iOS e Android. Successivamente, il JavaScript che gestisce upload e download può essere corretto e migliorato con Live updates di Capgo. Per il resto della migrazione da Cordova a Capacitor, consulta la nostra guida alla migrazione, e per altri SDK nativi legacy o a pagamento, le alternative ai plugin Ionic Enterprise.