iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Filesystem

Capacitor’s Filesystem plugin gives you cross-platform read/write access to app-private and shared storage on iOS, Android, and the web. Use it for caching, downloads, generated files, and offline data — pair with secure storage for sensitive content.

API, paths, permissions, sharing

EXAMPLE
// 1) Install
// npm install @capacitor/filesystem
// npx cap sync

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

// 2) Write a text file
await Filesystem.writeFile({
    path: 'notes.txt',
    data: 'Hello world',
    directory: Directory.Data,                       // app's private storage
    encoding: Encoding.UTF8,
    recursive: true,                                  // create parent dirs if missing
});

// 3) Read it back
const result = await Filesystem.readFile({
    path: 'notes.txt',
    directory: Directory.Data,
    encoding: Encoding.UTF8,
});
console.log(result.data);                            // 'Hello world'

// 4) Directories — where things live
// Directory.Data           — app's private documents (most use cases)
// Directory.Cache          — app cache (system may clear)
// Directory.External       — external storage (Android only; requires permission)
// Directory.ExternalStorage — shared external (downloads, photos)
// Directory.Library        — iOS Library directory
// Directory.Documents       — iOS Documents (visible in Files app)

await Filesystem.writeFile({ path: 'photo.jpg', data: base64Data, directory: Directory.Data });

// 5) Read JSON config
async function loadConfig() {
    try {
        const r = await Filesystem.readFile({
            path: 'config.json',
            directory: Directory.Data,
            encoding: Encoding.UTF8,
        });
        return JSON.parse(r.data as string);
    } catch {
        return { theme: 'system', firstRun: true };
    }
}

async function saveConfig(config) {
    await Filesystem.writeFile({
        path: 'config.json',
        data: JSON.stringify(config),
        directory: Directory.Data,
        encoding: Encoding.UTF8,
    });
}

// 6) Binary files — base64 encoding
import { Capacitor } from '@capacitor/core';
async function saveImage(blob: Blob) {
    const reader = new FileReader();
    return new Promise<string>((resolve, reject) => {
        reader.onload = async () => {
            const base64 = (reader.result as string).split(',')[1];
            const r = await Filesystem.writeFile({
                path: `images/${Date.now()}.jpg`,
                data: base64,
                directory: Directory.Data,
                recursive: true,
            });
            resolve(r.uri);                               // file:// uri
        };
        reader.onerror = reject;
        reader.readAsDataURL(blob);
    });
}

// 7) Directory operations
await Filesystem.mkdir({ path: 'cache/images', directory: Directory.Data, recursive: true });

const entries = await Filesystem.readdir({ path: 'cache/images', directory: Directory.Data });
for (const entry of entries.files) {
    console.log(entry.name, entry.size, entry.uri, entry.type);
}

await Filesystem.rmdir({ path: 'cache/images', directory: Directory.Data, recursive: true });

// 8) Delete a file
await Filesystem.deleteFile({ path: 'notes.txt', directory: Directory.Data });

// 9) Append, copy, move
await Filesystem.appendFile({
    path: 'log.txt',
    data: 'New line\n',
    directory: Directory.Data,
    encoding: Encoding.UTF8,
});

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

await Filesystem.rename({
    from: 'notes.txt',
    to:   'archived-notes.txt',
    directory: Directory.Data,
    toDirectory: Directory.Data,
});

// 10) Stat — metadata
const stat = await Filesystem.stat({ path: 'notes.txt', directory: Directory.Data });
console.log(stat.size, stat.mtime, stat.type);

// 11) URIs for display
// Use the uri returned by writeFile in <Image src=...> after conversion via Capacitor.convertFileSrc()
import { Capacitor } from '@capacitor/core';
const displayPath = Capacitor.convertFileSrc(r.uri);
// Use displayPath in <img>, <video>, or as href.

// 12) Permissions — Android shared storage
// External writes on Android 10+ need scoped storage; reach to MediaStore via the
// @capacitor-community/media or @capacitor/share plugins.
// Internal app storage (Directory.Data, .Cache) needs NO runtime permission.

// On older Android you may need:
// <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
// <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

// 13) Download to local file
import { Http } from '@capacitor/http';

async function download(url, name) {
    const res = await Http.get({ url, responseType: 'blob' });   // base64 string
    await Filesystem.writeFile({
        path: name,
        data: res.data,
        directory: Directory.Data,
    });
}

// 14) Share a file
import { Share } from '@capacitor/share';

const f = await Filesystem.getUri({ path: 'report.pdf', directory: Directory.Data });
await Share.share({
    title: 'My report',
    url: f.uri,
});

// 15) Web fallback
// On the web, Filesystem uses IndexedDB under the hood. Files persist per-origin.
// No access to the user's real filesystem — that's intentional.
// For downloads on web, use the URL.createObjectURL pattern + a <a download> link.

// 16) Storage budgets
// • iOS: ~5 GB practical limit on Documents (purgeable in low-space)
// • Android: less constrained but respect user's storage; show usage if you accumulate data
// • Web (IndexedDB): origin quota; budget varies by browser (~ tens of MB to several GB)

// 17) Cache hygiene
// Use Directory.Cache for derived data that can be re-fetched.
// Set up a periodic cleanup that deletes files older than N days:
const dir = await Filesystem.readdir({ path: 'cache', directory: Directory.Data });
const now = Date.now();
for (const f of dir.files) {
    if (now - f.mtime > 30 * 86400 * 1000) {
        await Filesystem.deleteFile({ path: `cache/${f.name}`, directory: Directory.Data });
    }
}

// 18) Common bugs
// • Forgetting recursive: true when writing nested paths → 'Parent directory missing'
// • Mixing base64 and binary directly — base64 is required by the plugin
// • Reading huge files into memory → use streaming (read chunks) or restructure
// • Saving sensitive data to Documents/Downloads → visible to other apps; use Directory.Data
// • iOS .Documents visible to user in Files app — opt out via UIFileSharingEnabled if undesired
// • Storing media in Directory.Cache then expecting persistence → system may clear
// • Forgetting Capacitor.convertFileSrc when displaying — image won't load due to scheme
// • Web origin changes (different port, host) → orphaned IndexedDB files
// • Permission denied on Android sharing → use MediaStore via community plugin
// • Cleaning up after deletion failures → check error codes, don't crash

Why it matters

Capacitor Filesystem covers cross-platform file I/O with sensible directory roots: Directory.Data for private app storage, Directory.Cache for derived data, Directory.Documents when the user should see them. Encode binary as base64, convert URIs with Capacitor.convertFileSrc for display, and reach for community plugins (MediaStore) for shared Android storage.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
await Filesystem.writeFile({ path: 'note.txt', data: 'hi', directory: Directory.Documents, encoding: Encoding.UTF8 });
Try it Yourself »

Discussion

Loading…