File handling Capacitor pada dasarnya terdiri dari beberapa komponen utama: @capacitor/filesystem untuk read dan write file di direktori app, Capacitor.convertFileSrc untuk menampilkannya, picker untuk impor file user, serta plugin native untuk download, upload, open, dan share. Panduan ini membahas semuanya dengan Capacitor 8 code, menjelaskan direktori mana yang harus digunakan di iOS dan Android, serta membahas masalah memory dan permission yang sering memicu bug file.
Toolbox penanganan file
| Tugas | Plugin |
|---|---|
| Read, write, list, copy, delete di app storage | @capacitor/filesystem |
Tampilkan file lokal di <img>, <video>, <iframe> |
Capacitor.convertFileSrc (core) |
| Biarkan user memilih file atau folder | @capgo/capacitor-file-picker |
| Download dengan progress | @capacitor/file-transfer atau @capgo/capacitor-downloader untuk background, pausable downloads |
| Upload dengan progress | @capacitor/file-transfer atau @capgo/capacitor-uploader untuk upload di background |
| Bagikan file atau simpan ke Downloads | @capgo/capacitor-file-sharer atau @capacitor/share |
| Buka file di aplikasi lain | @capacitor-community/file-opener |
| Kompres gambar | @capgo/capacitor-file-compressor |
| Zip dan unzip | @capgo/capacitor-zip |
Install plugin core terlebih dahulu:
bun add @capacitor/filesystem
bunx cap sync
Pilih direktori yang tepat
Setiap panggilan Filesystem membutuhkan directory. Salah memilih metode adalah penyebab paling umum file hilang dan aplikasi ditolak.
Directory |
Lokasi iOS | Lokasi Android | Dicadangkan | Gunakan untuk |
|---|---|---|---|---|
Data |
Documents |
Direktori file aplikasi (/data/data/<pkg>/files) |
iCloud iOS, Auto Backup Android | Data milik aplikasi |
Documents |
Documents (sama dengan Data) |
Publik Documents folder |
Ya | Hindari di Android 11+, lihat di bawah |
Library |
Library |
Direktori file aplikasi | iOS ya | File pendukung app, database |
LibraryNoCloud |
Library/NoCloud |
Direktori file app | Tidak | Konten besar, bisa diunduh ulang |
Cache |
Library/Caches |
Direktori cache app | Tidak | File sementara, bisa dihapus OS |
External |
Documents |
Direktori file eksternal app | Tidak | File app besar di Android, terhapus saat uninstall |
ExternalStorage |
Documents |
Root penyimpanan bersama | n/a | Hanya untuk versi lama, tidak bisa di Android 11+ |
Aturan dasarnya:
- Media besar yang bisa diunduh ulang (video offline, tile peta) simpan di
LibraryNoCloud. Apple menolak aplikasi yang menyimpan konten besar yang bisa diunduh ulang di folder berbasis iCloud, sesuai iOS Data Storage Guidelines. - File sementara (ekspor sekali pakai, thumbnail) simpan di
Cache. Jangan pernah menyimpan satu-satunya salinan data user di sana. - File yang harus bisa ditemukan user di luar aplikasi tidak boleh disimpan di direktori aplikasi. Simpan melalui share sheet, MediaStore Downloads, atau sistem file picker. Lihat panduan kami tentang Android scoped storage di Capacitor.
Menulis 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,
});
Skill encoding opsi menentukan bagaimana data diinterpretasikan. Dengan Encoding.UTF8, string ditulis sebagai teks. Tanpa itu, platform native men-decode data sebagai base64 dan menulis raw bytes. Lupa ini adalah bug klasik “PDF saya corrupt”.
Untuk menambahkan ke file yang sudah ada, seperti log, gunakan appendFile dengan opsi yang sama.
Membaca 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,
});
Cek apakah file ada dengan stat, yang akan reject jika file tidak ditemukan:
async function exists(path: string) {
try {
await Filesystem.stat({ path, directory: Directory.Data });
return true;
} catch {
return false;
}
}
Baca file besar dalam chunk
readFile mengembalikan seluruh file sebagai satu string base64. Untuk video berukuran 200 MB, ini berarti penggunaan memori JS sebesar ratusan MB untuk string tersebut. readFileInChunks gunakan streaming sebagai gantinya:
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
},
);
Lebih baik lagi, hindari memindahkan byte melalui JS sama sekali. Kebanyakan tugas "baca file besar" sebenarnya adalah "upload" atau "tampilkan", dan keduanya punya jalur native di bawah.
List, copy, move, dan delete
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 dan rename menerima toDirectory untuk pindah antar direktori, contohnya dari Cache ke Data setelah download selesai. Mereka juga menerima path absolut file:// saat Anda mengosongkan directory, yang merupakan cara Anda mengimpor file dari plugin lain.
Tampilkan file di WebView
Hindari memuat gambar atau video sebagai base64 data URL jika memungkinkan. Ubah native path menjadi URL yang disajikan oleh server lokal Capacitor:
import { Capacitor } from '@capacitor/core';
const { uri } = await Filesystem.getUri({ path: 'images/logo.png', directory: Directory.Data });
img.src = Capacitor.convertFileSrc(uri);
Di iOS, hasilnya terlihat seperti capacitor://localhost/_capacitor_file_/..., di Android https://localhost/_capacitor_file_/.... Ini berfungsi untuk <img>, <video>, <audio>, dan fetch(), yang berarti Anda juga bisa membaca file sebagai ArrayBuffer tanpa base64:
const buffer = await fetch(Capacitor.convertFileSrc(uri)).then((r) => r.arrayBuffer());
Simpan path, bukan URL yang sudah dikonversi. Bentuk konversi hanya valid untuk sesi aplikasi saat ini.
Pilih file dari perangkat
System picker memberikan kontrol kepada pengguna dan tidak memerlukan izin penyimpanan pada Android 13+ atau 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);
}
Tersedia juga pickImages, pickVideos, pickMedia dan pickDirectory, serta convertHeicToJpeg di iOS. Di iOS, types hanya menerima MIME type, bukan ekstensi.
File yang dipilih mungkin berada di lokasi sementara atau di balik content:// URI yang hanya bisa dibaca untuk sementara. Jika Anda butuh file tersebut nanti, segera salin ke direktori Anda sendiri dengan Filesystem.copy.
Download file
Dengan progress: @capacitor/file-transfer
Filesystem.downloadFile sudah deprecated di Filesystem 8. Penggantinya akan melakukan streaming response langsung ke disk:
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();
Di background: @capgo/capacitor-downloader
Untuk file besar yang harus tetap terdownload saat user keluar dari aplikasi, serta bisa di-pause dan di-resume, gunakan downloader Capgo, yang mengandalkan DownloadManager di Android dan background URLSession di 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
});
Sebuah relatif destination akan mengarah ke direktori file eksternal aplikasi di Android dan Documents direktori di iOS, lokasi yang sama dengan Directory.External.
Upload file
Jangan baca file ke base64 hanya untuk mengirimnya. Kedua opsi upload membaca langsung dari disk secara native:
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);
Untuk upload yang harus tetap berjalan saat aplikasi masuk ke background (video, batch upload foto), gunakan @capgo/capacitor-uploader, yang berjalan di background URLSession pada iOS dan layanan upload dengan notifikasi progress di 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 URL ke object storage adalah pola paling andal untuk file besar: API Anda menandatangani URL, dan perangkat langsung melakukan upload ke storage.
Bagikan atau ekspor file
Agar user bisa mengirim file ke aplikasi lain atau menyimpannya di tempat yang mudah ditemukan:
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',
});
Capgo file sharer menyalin file ke folder cache sendiri dan menyajikannya melalui FileProvider, jadi Anda tidak perlu mengedit file_paths.xml. With the official @capacitor/shareresmi, Android hanya bisa berbagi dari folder cache kecuali Anda menambahkan folder lain ke android/app/src/main/res/xml/file_paths.xml.
Buka file di aplikasi lain
bun add @capacitor-community/file-opener
bunx cap sync
import { FileOpener } from '@capacitor-community/file-opener';
await FileOpener.open({ filePath: uri, contentType: 'application/pdf' });
On iOS this opens a Quick Look preview. On Android it opens the default app for the MIME type or shows a chooser. If no app can handle the type, the call rejects, so catch it and offer sharing instead.
Recipe file umum
Generate PDF: render HTML dengan @capgo/capacitor-pdf-generator atau buat di JS dengan pdf-lib, lalu tulis output base64 dengan writeFile. Our tutorial scan ke PDF menunjukkan alur lengkapnya.
Hapus GPS dari foto sebelum upload: @capgo/capacitor-file-compressor menghapus metadata EXIF di setiap platform saat resize.
Resize atau konversi gambar: kompresor yang sama menangani lebar, tinggi, kualitas, dan format output, termasuk HEIC ke JPEG.
Bundle banyak file: zip sebuah folder dengan @capgo/capacitor-zip dan upload satu arsip alih-alih ratusan request.
Dukungan Web
@capacitor/filesystem bekerja di web menggunakan IndexedDB, jadi code Anda berjalan di browser selama pengembangan. Perbedaan yang perlu diperhatikan: pembacaan biner mengembalikan sebuah Blob, file hanya tersimpan di profil browser, dan kuota penyimpanan jauh lebih rendah. Untuk web build, gunakan URL.createObjectURL(blob) untuk tampilan.
Pemecahan Masalah
“File tidak ditemukan”: Anda mencampur direktori, atau menggunakan path absolut bersama dengan directory. Gunakan salah satu saja.
File biner korup: Anda menulis base64 dengan Encoding.UTF8. Hapus encoding-nya.
App crash saat file besar: base64 melalui bridge. Gunakan chunks, file-transfer, atau fetch(convertFileSrc(uri)).
Gambar tidak muncul di Android: Anda menggunakan file:// URL secara langsung. Konversi dengan Capacitor.convertFileSrc.
Failed to find configured root saat berbagi di Android: file berada di luar path di file_paths.xml. Gunakan Capgo file sharer atau salin ke Cache first.
File hilang di iOS: file berada di Cache, yang akan dihapus iOS saat storage penuh.
Penolakan App Store untuk penyimpanan iCloud: konten download yang besar di Data atau Documents. Pindahkan ke LibraryNoCloud.
Next steps
Sebagian besar logika file, seperti lokasi penyimpanan, aturan retry, dan penamaan, berada di JavaScript dan dapat disesuaikan dengan live updates Capgo setelah rilis. Untuk sisi Android terkait shared storage dan izin, silakan baca panduan scoped storagekami, dan lihat dokumentasi file picker untuk setiap opsi picker.