El manejo de archivos Capacitor se reduce a unos pocos bloques de construcción: @capacitor/filesystem para leer y escribir archivos en directorios de la aplicación Capacitor.convertFileSrc para mostrarlos, un selector para importar archivos de usuario, y plugins nativos para descargar, subir, abrir y compartir. Esta guía muestra cada uno de ellos con Capacitor 8 code, explica qué directorio utilizar en iOS y Android, y cubre los trampas de memoria y permisos que causan la mayoría de los errores de archivo.
La herramienta de manejo de archivos
| Tarea | Plugin |
|---|---|
| Read, write, list, copy, delete in app storage | @capacitor/filesystem |
Mostrar un archivo local en <img>, <video>, <iframe> |
Capacitor.convertFileSrc (núcleo) |
| Deje al usuario elegir archivos o una carpeta | @capgo/capacitor-file-picker |
| Descargar con progreso | @capacitor/file-transfer o @capgo/capacitor-downloader para descargas de fondo, pausables |
| Subir con progreso | @capacitor/file-transfer o @capgo/capacitor-uploader para subir archivos de fondo |
| Compartir un archivo o guardar en Descargas | @capgo/capacitor-file-sharer o @capacitor/share |
| Abrir un archivo en otra aplicación | @capacitor-community/file-opener |
| Comprimir imágenes | @capgo/capacitor-file-compressor |
| Comprimir imágenes | @capgo/capacitor-zip |
Instale el plugin principal primero:
bun add @capacitor/filesystem
bunx cap sync
Instale el plugin de base primero:
Elige el directorio correcto directoryElige el incorrecto es la causa más común de archivos perdidos y aplicaciones rechazadas.
Directory |
iOS location | Ubicación de iOS | Resguardado | Usar para |
|---|---|---|---|---|
Data |
Documents |
Directorio de archivos de la aplicación (/data/data/<pkg>/files) |
iOS iCloud, Android Copia de Seguridad de Auto | Datos del usuario que pertenecen a la aplicación |
Documents |
Documents (igual que Data) |
Public Documents carpeta |
Sí | Evitar en Android 11+, ver abajo |
Library |
Library |
Directorio de archivos de la aplicación | iOS sí | Archivos de soporte de la aplicación, bases de datos |
LibraryNoCloud |
Library/NoCloud |
Directorio de archivos de la aplicación | No | Contenido grande, re-descargable |
Cache |
Library/Caches |
Directorio de caché de la aplicación | No | Temporary files, can be wiped by the OS |
External |
Documents |
Directorio de archivos externos de la aplicación | No | Archivos grandes de la aplicación en Android, eliminados al desinstalar |
ExternalStorage |
Documents |
Raíz de almacenamiento compartido | n/a | Sólo legado, no accesible en Android 11+ |
Reglas de dedo:
- Contenido de medios re-descargable grande (offline videos, map tiles) se almacenan
LibraryNoCloudApple rechaza aplicaciones que almacenan contenido descargable grande en carpetas respaldadas por iCloud, como se describe en las Directrices de almacenamiento de datos de iOS. - Archivos temporales (los exports que compartas una vez, miniaturas) van en
Cache. Nunca almacene la única copia de datos del usuario allí. - Archivos que el usuario debe encontrar más tarde fuera de la aplicación No pertenecen a ninguna carpeta de la aplicación. Guardelos a través de la hoja de compartir, MediaStore Descargas, o el selector de archivos del sistema. Consulte nuestra guía a Almacenamiento de Android escogido en Capacitor.
Escrbir un archivo
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,
});
La encoding La opción decide cómo data se interpreta. Con Encoding.UTF8, la cadena se escribe como texto. Sin ella, las plataformas nativas decodifican data como base64 y escriben bytes crudos. Olvidar esto es el clásico ‘mi PDF está corrupto’.
Para agregar a un archivo existente, como un registro, utilice appendFile con las mismas opciones.
Leer un archivo
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,
});
Compruebe si existe un archivo con stat, que rechaza cuando el archivo no existe:
async function exists(path: string) {
try {
await Filesystem.stat({ path, directory: Directory.Data });
return true;
} catch {
return false;
}
}
Leer archivos grandes en lotes
readFile devuelve el archivo completo como una cadena base64. Para un video de 200 MB significa cientos de MB de cadenas en memoria JS. readFileInChunks en lugar de eso:
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
},
);
Mejor aún, evite mover los bytes a través de JS en absoluto. La mayoría de las tareas de 'leer un gran archivo' en realidad son 'subirlo' o 'mostrarlo', y ambos tienen rutas nativas debajo.
Listar, copiar, mover y eliminar
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 y rename aceptar toDirectory para moverse entre directorios, por ejemplo desde Cache para Data después de completar un descarga. También aceptan rutas absolutas file:// rutas cuando omitas directory, que es cómo importar archivos devueltos por otros plugins.
Muestra un archivo en la WebView
Jamás cargue imágenes o videos como URLs de datos base64 si puede evitarlo. Convirtiendo la ruta nativa a una URL servida por el servidor local de Capacitor:
import { Capacitor } from '@capacitor/core';
const { uri } = await Filesystem.getUri({ path: 'images/logo.png', directory: Directory.Data });
img.src = Capacitor.convertFileSrc(uri);
On iOS, el resultado es similar a capacitor://localhost/_capacitor_file_/..., en Android https://localhost/_capacitor_file_/.... Funciona para <img>, <video>, <audio>, y fetch(), lo que significa que también puedes leer un archivo como un ArrayBuffer sin base64:
const buffer = await fetch(Capacitor.convertFileSrc(uri)).then((r) => r.arrayBuffer());
Almacena rutas, no URLs convertidas. La forma convertida solo es válida para la sesión de la aplicación actual.
Selecciona archivos desde el dispositivo
El selector del sistema da al usuario control y no necesita permiso de almacenamiento en 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);
}
Existen también pickImages, pickVideos, pickMedia y pickDirectory, además convertHeicToJpeg en iOS. En iOS, types solo acepta tipos MIME, no extensiones.
Los archivos seleccionados pueden residir en una ubicación temporal o detrás de un content:// URI que solo permanece legible durante un tiempo. Si necesita el archivo más tarde, copie en su propio directorio de inmediato con Filesystem.copy.
Descargar archivos
Con progreso: @capacitor/transferencia-de-archivo
Filesystem.downloadFile Se ha descontinuado en Filesystem 8. La reemplazo transmite la respuesta directamente a disco:
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();
En segundo plano: @capgo/descargador-capacitor
Para archivos grandes que deben seguir descargándose cuando el usuario abandone la aplicación, y que pueden ser pausados y reanudados, utilice el descargador Capgo, que se basa en DownloadManager en Android y segundo plano URLSession en 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
});
Un relativo destination se resuelve a la carpeta de archivos externos del aplicación en Android y Documents la misma carpeta en iOS, los lugares mismos donde Directory.External.
Subir archivos
No lea un archivo en base64 solo para enviarlo. Ambas opciones de subida leen desde el disco nativamente:
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);
Para subidas que deben sobrevivir a que la aplicación se ponga en segundo plano (videos, subidas en batch de fotos), utilice @capgo/capacitor-uploaderque se ejecuta en segundo plano URLSession en iOS y un servicio de subida con una notificación de progreso en 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 URLs para almacenamiento de objetos son el patrón más confiable para archivos grandes: tu API firma la URL, y el dispositivo sube directamente al almacenamiento.
Compartir o exportar un archivo
Para permitir que el usuario envíe un archivo a otra aplicación o lo guarde en un lugar donde pueda encontrarlo:
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',
});
El compartidor de archivos Capgo copia el archivo en su propia carpeta de caché y lo sirve a través de un servidor dedicado. FileProvider, por lo tanto no necesitas editar file_paths.xmlCon la versión oficial @capacitor/share, Android solo puede compartir desde la carpeta de caché a menos que agregues otras carpetas a android/app/src/main/res/xml/file_paths.xml.
Abrir un archivo en otra aplicación
bun add @capacitor-community/file-opener
bunx cap sync
import { FileOpener } from '@capacitor-community/file-opener';
await FileOpener.open({ filePath: uri, contentType: 'application/pdf' });
En iOS, esto abre una vista previa de Quick Look. En Android, abre la aplicación por defecto para el tipo MIME o muestra un selector. Si ninguna aplicación puede manejar el tipo, la llamada rechaza, así que atrapa la excepción y ofrece compartir en su lugar
Recetas de archivos comunes
Generar un PDF: renderizar HTML con @capgo/capacitor-pdf-generator o constrúyelo en JS con pdf-lib, luego escribe la salida base64 con writeFileNuestra tutorial de escaneo a PDF muestra el flujo completo.
Quitar GPS de una foto antes de subirla: @capgo/capacitor-file-compressor elimina los metadatos EXIF en todas las plataformas mientras se redimensiona.
Redimensiona o convierte una imagen.: el mismo compresor maneja ancho, alto, calidad y formato de salida, incluyendo HEIC a JPEG.
Bundle muchos archivos.: zip una carpeta con @capgo/capacitor-zip y sube un archivo en lugar de cientos de solicitudes.
Soporte web
@capacitor/filesystem funciona en la web utilizando IndexedDB, por lo que tu code se ejecuta en el navegador durante el desarrollo. Ten en cuenta las siguientes diferencias: las lecturas binarias devuelven un Blob, los archivos solo existen en el perfil del navegador, y las cuotas de almacenamiento son mucho más bajas. Para compilaciones web, prefiere URL.createObjectURL(blob) para la visualización.
Troubleshooting
“El archivo no existe”: mezclaste directorios, o pasaste una ruta absoluta junto con un directory: Utiliza uno u otro.
Archivos binarios dañados: escribiste base64 con Encoding.UTF8: Elimina la codificación.
La aplicación se bloquea con archivos grandes: base64 a través del puente. Utiliza trozos, transferencia de archivos, o fetch(convertFileSrc(uri)).
La imagen no se muestra en Android: utilizaste una file:// Convertirlo directamente a la URL. Capacitor.convertFileSrc.
Failed to find configured root When compartiendo en Android: el archivo está fuera de los caminos en file_paths.xml. Utilice el compartidor de archivos Capgo o copie a Cache primero.
Los archivos desaparecen en iOS: estaban en Cache, que iOS elimina bajo presión de almacenamiento.
Rechazo de la App Store para almacenamiento en iCloud: contenido descargable grande en Data o Documents. Move it to LibraryNoCloud.
Próximos pasos
La mayoría de la lógica de archivo, como dónde almacenar cosas, reglas de reintento y nombres, viven en JavaScript y se pueden ajustar con Capgo actualizaciones en vivo después de la liberación. Para el lado Android de almacenamiento compartido y permisos, continúa con nuestra guía de almacenamiento en ámbitoy consulta los documentos del selector de archivos para cada opción de selector.