Lompat ke konten utama

Panduan File Handling Capacitor: Read, Write, Pick, Share

Panduan file handling Capacitor: baca dan tulis file, pilih direktori, pick, tampilkan, buka, share, download dan upload file di iOS dan Android.

Panduan File Handling Capacitor: Read, Write, Pick, Share

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.

Live update untuk aplikasi Capacitor

Saat bug di web layer sudah live, kirim perbaikannya lewat Capgo alih-alih menunggu berhari-hari untuk persetujuan app store. Pengguna menerima update di background, sementara perubahan native tetap di jalur review normal.

Terbaru dari Blog kami

Capgo memberikan insight terbaik yang Anda butuhkan untuk membuat aplikasi mobile yang benar-benar profesional.