Salta al contenuto principale

Alternativa a cordova-plugin-file-transfer per Capacitor

Sostituisci cordova-plugin-file-transfer nelle app Capacitor: @capacitor/file-transfer per i trasferimenti semplici, uploader e downloader Capgo per i job in background.

Illustrazione della migrazione di un'app Cordova a Capacitor, usata per una guida al plugin di trasferimento file

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 XMLHttpRequest Livello 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-file e il suo cdvfile:// e file:// modello path,
  • non si adatta a Capacitor Filesystem conversioni 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. fetch e XHR ti danno un Blob oppure ArrayBuffer nel 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/filesystem non 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.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.