Salta al contenuto principale

Guida alla gestione dei file con Capacitor: Read, Write, Pick, Share

Guida alla gestione dei file con Capacitor: leggere e scrivere file, scegliere directory, selezionare, visualizzare, aprire, condividere, scaricare e caricare file su iOS e Android.

Lettura, scrittura e condivisione di file in un'app mobile Capacitor

La gestione dei file in Capacitor si basa su alcuni elementi fondamentali: @capacitor/filesystem per leggere e scrivere file nelle directory dell'app, Capacitor.convertFileSrc per visualizzarli, un picker per importare i file dell'utente e plugin nativi per scaricare, caricare, aprire e condividere. Questa guida mostra ognuno di essi con Capacitor 8 code, spiega quale directory usare su iOS e Android e copre le insidie legate alla memoria e ai permessi che causano la maggior parte dei bug relativi ai file.

Il toolkit per la gestione file

Attività Plugin
Read, write, list, copy, delete in app storage @capacitor/filesystem
Mostra un file locale in <img>, <video>, <iframe> Capacitor.convertFileSrc (core)
Lascia all'utente la scelta di file o cartelle @capgo/capacitor-file-picker
Download con avanzamento @capacitor/file-transfer oppure @capgo/capacitor-downloader per download in background, in pausa
Upload con avanzamento @capacitor/file-transfer oppure @capgo/capacitor-uploader per upload in background
Condividi un file o salvalo in Download @capgo/capacitor-file-sharer oppure @capacitor/share
Apri un file in un'altra app @capacitor-community/file-opener
Comprimi immagini @capgo/capacitor-file-compressor
Comprimi ed estrai zip @capgo/capacitor-zip

Installa prima il plugin principale:

bun add @capacitor/filesystem
bunx cap sync

Scegli la directory giusta

Ogni chiamata Filesystem richiede un directory. Scegliere quello sbagliato è la causa più comune di file persi e app rifiutate.

Directory Posizione iOS Posizione Android Backup Usa per
Data Documents Dir file app (/data/data/<pkg>/files) iCloud iOS, Auto Backup Android Dati utente di proprietà dell'app
Documents Documents (stesso di Data) Pubblico Documents cartella Sì Da evitare su Android 11+, vedi sotto
Library Library Dir file app iOS sì File di supporto app, database
LibraryNoCloud Library/NoCloud Dir file app No Contenuti grandi, ridownloadabili
Cache Library/Caches Dir cache app No File temporanei, possono essere eliminati dal SO
External Documents Directory file esterni dell'app No File di grandi dimensioni su Android, eliminati alla disinstallazione
ExternalStorage Documents Radice dello storage condiviso n/d Solo legacy, non accessibile su Android 11+

Regole generali:

  • Media di grandi dimensioni scaricabili (video offline, mappe) va in LibraryNoCloud. Apple rifiuta le app che mettono contenuti di grandi dimensioni scaricabili di nuovo in cartelle con backup iCloud, come descritto nelle sue iOS Data Storage Guidelines.
  • File temporanei (esportazioni condivise una volta, miniature) vanno in Cache. Non conservare mai l'unica copia dei dati utente lì.
  • I file che l'utente dovrebbe trovare in seguito fuori dalla tua app non appartengono affatto a una directory dell'app. Salvali tramite la share sheet, MediaStore Downloads o il file picker di sistema. Consulta la nostra guida su lo scoped storage di Android in Capacitor.

Scrivi un file

import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';

// Text file
await Filesystem.writeFile({
  path: 'notes/today.txt',
  data: 'Hello from Capacitor',
  directory: Directory.Data,
  encoding: Encoding.UTF8,
  recursive: true, // create notes/ if missing
});

// JSON
await Filesystem.writeFile({
  path: 'settings.json',
  data: JSON.stringify({ theme: 'dark' }),
  directory: Directory.Data,
  encoding: Encoding.UTF8,
});

// Binary file: pass base64 and no encoding
await Filesystem.writeFile({
  path: 'images/logo.png',
  data: base64Png,
  directory: Directory.Data,
  recursive: true,
});

Il encoding l'opzione decide come data viene interpretato. Con Encoding.UTF8, la stringa viene scritta come testo. Senza di essa, le piattaforme native decodificano data come base64 e scrivono byte grezzi. Dimenticarselo è il classico bug "il mio PDF è corrotto".

Per aggiungere a un file esistente, come un log, usa appendFile con le stesse opzioni.

Leggere un file

const { data } = await Filesystem.readFile({
  path: 'settings.json',
  directory: Directory.Data,
  encoding: Encoding.UTF8,
});
const settings = JSON.parse(data as string);

// Binary: returns base64 on native, a Blob on web
const image = await Filesystem.readFile({
  path: 'images/logo.png',
  directory: Directory.Data,
});

Verificare se un file esiste con stat, che rifiuta se il file non è presente:

async function exists(path: string) {
  try {
    await Filesystem.stat({ path, directory: Directory.Data });
    return true;
  } catch {
    return false;
  }
}

Leggere file grandi in chunk

readFile restituisce l'intero file come un'unica stringa base64. Per un video da 200 MB significa centinaia di MB di stringhe in memoria JS. readFileInChunks lo passa invece in streaming:

await Filesystem.readFileInChunks(
  { path: 'videos/clip.mp4', directory: Directory.Data, chunkSize: 1024 * 1024 },
  (chunk, err) => {
    if (err) return console.error(err);
    if (chunk === null) return console.log('done');
    processChunk(chunk.data); // base64 chunk
  },
);

Meglio ancora: evita del tutto di far passare i byte attraverso JS. La maggior parte dei task di "lettura di file pesanti" in realtà è "upload" o "visualizzazione", e per entrambi qui sotto ci sono percorsi nativi.

Elenca, copia, sposta ed elimina

const { files } = await Filesystem.readdir({ path: 'notes', directory: Directory.Data });
for (const f of files) console.log(f.name, f.type, f.size, f.mtime);

await Filesystem.mkdir({ path: 'archive', directory: Directory.Data, recursive: true });

await Filesystem.copy({
  from: 'notes/today.txt',
  to: 'archive/today.txt',
  directory: Directory.Data,
});

await Filesystem.rename({
  from: 'archive/today.txt',
  to: 'archive/2026-10-01.txt',
  directory: Directory.Data,
});

await Filesystem.deleteFile({ path: 'notes/today.txt', directory: Directory.Data });
await Filesystem.rmdir({ path: 'notes', directory: Directory.Data, recursive: true });

copy e rename accettare toDirectory per spostarsi tra le directory, ad esempio da Cache a Data una volta completato il download. Accettano anche percorsi file:// assoluti quando si omette directory, ed è così che si importano i file restituiti da altri plugin.

Visualizza un file nella WebView

Non caricare mai immagini o video come data URL base64 se puoi evitarlo. Converti il percorso nativo in un URL servito dal server locale di Capacitor:

import { Capacitor } from '@capacitor/core';

const { uri } = await Filesystem.getUri({ path: 'images/logo.png', directory: Directory.Data });
img.src = Capacitor.convertFileSrc(uri);

Su iOS il risultato è simile a capacitor://localhost/_capacitor_file_/..., su Android https://localhost/_capacitor_file_/.... Funziona per <img>, <video>, <audio>, e fetch(), il che significa che puoi anche leggere un file come ArrayBuffer senza base64:

const buffer = await fetch(Capacitor.convertFileSrc(uri)).then((r) => r.arrayBuffer());

Salva i path, non gli URL convertiti. La forma convertita è valida solo per la sessione corrente dell'app.

Seleziona file dal dispositivo

Il picker di sistema dà il controllo all'utente e non richiede permessi di storage su Android 13+ o iOS:

bun add @capgo/capacitor-file-picker
bunx cap sync
import { CapgoFilePicker } from '@capgo/capacitor-file-picker';

const { files } = await CapgoFilePicker.pickFiles({
  types: ['application/pdf', 'image/*'],
  limit: 5,
  readData: false, // keep false for anything bigger than a few MB
});

for (const file of files) {
  console.log(file.name, file.mimeType, file.size, file.path);
}

Ci sono anche pickImages, pickVideos, pickMedia e pickDirectory, e anche convertHeicToJpeg su iOS. Su iOS, types accetta solo i MIME type, non le estensioni.

I file selezionati potrebbero trovarsi in una posizione temporanea o dietro un content:// URI leggibile solo per un breve periodo. Se ti serve il file in seguito, copialo subito nella tua directory con Filesystem.copy.

Download dei file

Con progresso: @capacitor/file-transfer

Filesystem.downloadFile è deprecato in Filesystem 8. Il sostituto trasmette la risposta direttamente su disco in streaming:

bun add @capacitor/file-transfer
bunx cap sync
import { FileTransfer } from '@capacitor/file-transfer';
import { Directory, Filesystem } from '@capacitor/filesystem';

const { uri } = await Filesystem.getUri({ path: 'downloads/report.pdf', directory: Directory.Data });

const listener = await FileTransfer.addListener('progress', (p) => {
  if (p.lengthComputable) setProgress(p.bytes / p.contentLength);
});

await FileTransfer.downloadFile({
  url: 'https://example.com/report.pdf',
  path: uri,
  progress: true,
  headers: { Authorization: `Bearer ${token}` },
});

await listener.remove();

In background: @capgo/capacitor-downloader

Per i file di grandi dimensioni che devono continuare a scaricarsi quando l'utente esce dall'app e che possono essere messi in pausa e ripresi, usa il downloader Capgo, che si basa su DownloadManager su Android e in background URLSession su iOS:

import { CapacitorDownloader } from '@capgo/capacitor-downloader';

await CapacitorDownloader.addListener('downloadProgress', ({ id, progress }) => {
  console.log(id, progress);
});
await CapacitorDownloader.addListener('downloadCompleted', ({ id }) => {
  console.log('done', id);
});

await CapacitorDownloader.download({
  id: 'course-video-12',
  url: 'https://cdn.example.com/course/12.mp4',
  destination: 'videos/12.mp4',
  network: 'wifi-only',
  notification: 'progress', // Android
});

A relative destination viene risolto nella directory file esterna dell'app su Android e nella Documents directory su iOS, gli stessi luoghi di Directory.External.

Carica file

Non leggere un file in base64 solo per inviarlo. Entrambe le opzioni di upload leggono nativamente dal disco:

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

const result = await FileTransfer.uploadFile({
  url: 'https://api.example.com/upload',
  path: uri,          // full file:// path
  fileKey: 'file',
  mimeType: 'application/pdf',
  headers: { Authorization: `Bearer ${token}` },
  progress: true,
});
console.log(result.responseCode, result.bytesSent);

Per upload che devono sopravvivere al passaggio dell'app in background (video, upload batch di foto), usa @capgo/capacitor-uploader, che viene eseguito in background URLSession su iOS e un servizio di upload con notifica di avanzamento su Android:

import { Uploader } from '@capgo/capacitor-uploader';

const { id } = await Uploader.startUpload({
  filePath: uri,
  serverUrl: 'https://api.example.com/upload',
  method: 'PUT', // binary body, ideal for presigned S3 or R2 URLs
  mimeType: 'video/mp4',
});

Presigned PUT Gli URL verso l'object storage sono il pattern più affidabile per i file grandi: il tuo API firma l'URL e il dispositivo carica direttamente sullo storage.

Condividi o esporta un file

Per consentire all'utente di inviare un file a un'altra app o salvarlo in un luogo facilmente accessibile:

import { FileSharer } from '@capgo/capacitor-file-sharer';

await FileSharer.share({
  filename: 'report.pdf',
  path: uri,
  contentType: 'application/pdf',
});

// Android: write to Downloads via MediaStore. iOS: opens the share sheet ("Save to Files")
await FileSharer.save({
  filename: 'report.pdf',
  path: uri,
  contentType: 'application/pdf',
});

Il file sharer di Capgo copia il file nella propria cartella cache e lo serve tramite un FileProvider, quindi non devi modificare file_paths.xml. Con quello ufficiale @capacitor/share, Android può condividere solo dalla cartella cache, a meno che tu non aggiunga altre cartelle a android/app/src/main/res/xml/file_paths.xml.

Apri un file in un'altra app

bun add @capacitor-community/file-opener
bunx cap sync
import { FileOpener } from '@capacitor-community/file-opener';

await FileOpener.open({ filePath: uri, contentType: 'application/pdf' });

Su iOS questo apre un'anteprima Quick Look. Su Android apre l'app predefinita per il MIME type o mostra un selettore. Se nessuna app può gestire il tipo, la chiamata fallisce: intercetta l'errore e offri la condivisione come alternativa.

Ricette comuni per i file

Genera un PDF: renderizza HTML con @capgo/capacitor-pdf-generator o crealo in JS con pdf-lib, quindi scrivi l'output base64 con writeFile. Il nostro tutorial: da scan a PDF mostra il flusso completo.

Rimuovi i GPS da una foto prima dell'upload: @capgo/capacitor-file-compressor rimuove i metadati EXIF su ogni piattaforma durante il ridimensionamento.

Ridimensiona o converti un'immagine: lo stesso compressore gestisce larghezza, altezza, qualità e formato di output, incluso HEIC in JPEG.

Bundle di più file: zip una cartella con @capgo/capacitor-zip e carica un unico archivio invece di centinaia di richieste.

Supporto web

@capacitor/filesystem funziona sul web usando IndexedDB, quindi il tuo code gira nel browser durante lo sviluppo. Differenze da tenere a mente: le letture binarie restituiscono un Blob, i file esistono solo nel profilo del browser e le quote di storage sono molto più basse. Per le build web, preferisci URL.createObjectURL(blob) per la visualizzazione.

Risoluzione problemi

“File does not exist”: hai mescolato directory, o passato un percorso assoluto insieme a un directory. Usa l'uno o l'altro.

File binari corrotti: hai scritto base64 con Encoding.UTF8. Rimuovi l'encoding.

L'app si blocca con file di grandi dimensioni: base64 attraverso il bridge. Usa chunk, file-transfer o fetch(convertFileSrc(uri)).

L'immagine non viene visualizzata su Android: hai usato un file:// URL direttamente. Convertilo con Capacitor.convertFileSrc.

Failed to find configured root quando condividi su Android: il file è al di fuori dei percorsi in file_paths.xml. Usa il file sharer di Capgo o copia in Cache prima.

I file spariscono su iOS: erano in Cache, che iOS elimina in caso di pressione sulla memoria.

Rifiuto da parte dell'App Store per lo storage iCloud: contenuti scaricabili di grandi dimensioni in Data oppure Documents. Spostalo in LibraryNoCloud.

Prossimi passi

Gran parte della logica dei file, ad esempio dove salvi i file, le regole di retry e la denominazione, risiede in JavaScript e può essere modificata con Live updates di Capgo dopo il rilascio. Per lo storage condiviso e i permessi su Android, prosegui con la nostra guida sullo scoped storage, e dai un'occhiata alla documentazione del file picker per ogni opzione del picker.

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.