Lebih lanjut ke konten utama

Penggunaan Penyimpanan Terbatas di Aplikasi Capacitor

Penyimpanan Terbatas Android di Aplikasi Capacitor dijelaskan: apa yang berubah di Android 10 hingga 14, izin yang masih berfungsi, MediaStore, SAF, dan direktori aman.

Kredit Artikel

Martin Donadieu

Pengarang

Valeria

Pengulas

Jordan

Pengedit

Penyimpanan Terbatas Android di Aplikasi Capacitor

Penyimpanan Terbatas Android membatasi apa yang dapat dibaca dan ditulis oleh aplikasi Capacitor di luar folder sendiri. Sejak Android 10, dan sepenuhnya sejak Android 11, aplikasi dapat menggunakan direktori pribadi secara bebas, dapat menambahkan file ke koleksi bersama melalui MediaStore, dan memerlukan pengguna untuk memilih apa pun lainnya melalui Framework Akses Penyimpanan. Panduan ini menjelaskan apa yang berubah per versi Android, nilai Capacitor yang masih berfungsi, dan cara menyimpan, mengimpor, dan mengexport file dengan cara yang didukung di Capacitor 8. Directory Nilai-nilai masih berfungsi, dan cara menyimpan, mengimport, serta mengexport file dengan cara yang didukung pada Capacitor 8.

Apa yang berubah, versi demi versi

Android API Perubahan yang berpengaruh bagi aplikasi Capacitor
10 29 Penyimpanan terbatas diperkenalkan. Aplikasi dapat menolaknya dengan requestLegacyExternalStorage="true" saat mengarahkan ke API 29
11 30 Penyimpanan Terbatas Diperlukan untuk Aplikasi yang Target 30+. requestLegacyExternalStorage diabaikan. WRITE_EXTERNAL_STORAGE tidak lagi memberikan akses apa pun. MANAGE_EXTERNAL_STORAGE diperkenalkan. Pemilih pohon tidak dapat memilih Root Download atau Root Volume
12 31-32 Tidak ada perubahan penyimpanan utama
13 33 READ_EXTERNAL_STORAGE diperlukan. READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIOTidak ada izin yang memberikan akses ke dokumen aplikasi lain
14 34 Akses foto parsial dengan READ_MEDIA_VISUAL_USER_SELECTED
15 dan 16 35-36 Tidak ada model penyimpanan baru, kebijakan Play pada izin foto dan video ditegakkan

Capacitor 8 target API 36, sehingga aplikasi Anda selalu berjalan dengan penyimpanan terbatas pada perangkat modern. Tidak ada flag untuk mendapatkan perilaku lama kembali.

Apa yang rusak pada versi lama Capacitor code

Code ditulis untuk Capacitor 2 atau 3, atau dipindahkan dari Cordova, sering gagal diam-diam pada perangkat baru:

  • Filesystem.writeFile({ directory: Directory.Documents }) atau Directory.ExternalStorage to “save to the phone”. On Android 11+ these paths point to shared storage, and writes fail or the file cannot be read back after reinstall.
  • Membaca jalur sembarang seperti /storage/emulated/0/Download/file.pdf dari sebuah jalur yang ditulis di pengaturan atau diterima dari server.
  • Requesting READ_EXTERNAL_STORAGE Mengakses daftar dokumen pengguna. Pada Android 13+, izin ini tidak berlaku untuk file non-media.
  • Memakai Filesystem.requestPermissions() dan menganggap granted berarti aplikasi dapat menulis di mana saja.
  • Membangun jalur file dari sebuah content:// URI yang dikembalikan oleh sebuah picker.

Apa yang masih berfungsi tanpa izin apa pun

Direktori aplikasi sendiri tidak terpengaruh oleh penyimpanan terbatas dan tidak memerlukan izin:

Capacitor Directory Rute Android Jalur Android Dapat dilihat oleh pengguna
Data, Library, LibraryNoCloud /data/data/<package>/files No Ya
Cache /data/data/<package>/cache Belum Ya
External /storage/emulated/0/Android/data/<package>/files Hanya melalui USB atau beberapa pengelola file Ya
ExternalCache /storage/emulated/0/Android/data/<package>/cache Sama Ya
Documents Dibagikan Documents Folder bersama Ya Belum
ExternalStorage Root penyimpanan bersama Ya Belum

Empat baris pertama aman. Documents dan ExternalStorage adalah baris warisan: sejak Android 11 aplikasi Anda hanya dapat melihat file di sana yang dibuat sendiri, dan setelah reinstall bahkan kehilangan akses bahkan ke mereka. Tangan mereka sebagai tidak mendukung untuk code baru.

External berguna untuk file besar (video offline, peta) karena mungkin hidup di partisi yang lebih besar, dan file di sana tidak termasuk Auto Backup. Sejak Android 11, aplikasi lain tidak dapat membaca Android/data, sehingga masih privat secara praktis.

Menyimpan file yang dapat ditemukan pengguna: MediaStore

Ketika pengguna mengetuk “Download” pada faktur, mereka mengharapkan di folder Downloads. Pada Android 10+, aplikasi dapat memasukkan file ke koleksi bersama melalui MediaStore tanpa izin apa pun. Downloads, Pictures, Movies, Music dan Documents mengakses koleksi melalui MediaStore tanpa izin apa pun.

@capgo/capacitor-file-sharer mengelilingi ini dengan save():

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

const { uri } = await Filesystem.getUri({ path: 'exports/invoice-1042.pdf', directory: Directory.Data });

const result = await FileSharer.save({
  filename: 'invoice-1042.pdf',
  path: uri,
  contentType: 'application/pdf',
  android: {
    saveDirectory: 'downloads',     // 'downloads' | 'pictures' | 'movies' | 'music' | 'documents'
    relativePath: 'MyApp/Invoices', // subfolder, Android 10+
  },
});

console.log(result.uri); // content:// URI of the saved file

On iOS, panggilan yang sama membuka sheet berbagi, di mana pengguna memilih “Simpan ke File”, sehingga satu code jalur menutupi kedua platform.

On Android 9 dan lebih rendah, menulis ke penyimpanan bersama masih memerlukan WRITE_EXTERNAL_STORAGE. Deklarasikan hanya untuk versi-versi tersebut:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28" />

Capacitor 8 mendukung Android 7 (API 24) dan di atasnya, jadi jika Anda masih mengirimkan ke perangkat Android 7 hingga 9, jangan lupa garis itu.

Foto dan video

Untuk media, prefer aliran platform daripada menulis file sendiri. @capacitor/camera menulis ke galeri dengan saveToGallery: true, dan hanya memerlukan izin penyimpanan lama pada versi Android yang lebih lama. Lihat panduan kami untuk mengambil dan mengedit foto di Capacitor.

Membaca file pengguna: pilih file daripada izin

Penyimpanan terbatas mengharapkan pengguna untuk memilih file. Pemilih Android mengembalikan content:// URI yang aplikasi Anda dapat membaca meskipun tidak memiliki izin penyimpanan.

import { CapgoFilePicker } from '@capgo/capacitor-file-picker';
import { Filesystem, Directory } from '@capacitor/filesystem';

const { files } = await CapgoFilePicker.pickFiles({ types: ['application/pdf'], limit: 1 });
const picked = files[0];

// Copy into app storage right away: picker grants can expire
await Filesystem.copy({
  from: picked.path!,
  to: `imports/${picked.name}`,
  toDirectory: Directory.Data,
});

Untuk foto dan video, @capgo/capacitor-file-pickers pickMedia dan @capacitor/cameras chooseFromGallery gunakan Picker Foto Android di Android 13+, yang tidak memerlukan izin sama sekali.

Hindari izin akses media baca

Google Play memiliki kebijakan izin foto dan video yang hanya memungkinkan READ_MEDIA_IMAGES dan READ_MEDIA_VIDEO for apps whose core purpose needs broad gallery access, such as gallery apps or photo editors. Everyone else must use the picker and declare that in Play Console. If a plugin you use declares these permissions, remove them from the merged manifest:

<uses-permission
    android:name="android.permission.READ_MEDIA_IMAGES"
    tools:node="remove" />
<uses-permission
    android:name="android.permission.READ_MEDIA_VIDEO"
    tools:node="remove" />

Add xmlns:tools="http://schemas.android.com/tools" Tambahkan <manifest> Tandai pertama.

Akses Folder dengan Framework Akses Penyimpanan

Beberapa aplikasi memerlukan akses berkelanjutan ke folder: aplikasi catatan yang sinkron dengan folder pengguna, folder ekspor untuk aplikasi lapangan. Framework Akses Penyimpanan (SAF) memungkinkan pengguna memberikan akses ke folder dengan ACTION_OPEN_DOCUMENT_TREE:

const { path } = await CapgoFilePicker.pickDirectory();
// On Android: a content://com.android.externalstorage.documents/tree/... URI

Tiga hal yang perlu diketahui:

  1. Hasilnya adalah URI pohon, bukan jalur. Anda tidak dapat melewatkan URI tersebut ke Filesystem.writeFile. File di dalamnya dibuat dan dibaca melalui DocumentsContract atau DocumentFile di dalam native code.
  2. Akses tidak bertahan setelah restart kecuali aplikasi Anda memanggil takePersistableUriPermission pada URI tersebut. Tambahkan metode native kecil jika Anda membutuhkannya:
@PluginMethod
public void persist(PluginCall call) {
    Uri uri = Uri.parse(call.getString("uri"));
    getContext().getContentResolver().takePersistableUriPermission(
        uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    call.resolve();
}
  1. Beberapa folder tidak dapat diberikan akses. On Android 11+, pilih file tidak menerima akar penyimpanan internal, akar folder, dan Download dan Android/data and Android/obbMinta pengguna membuat atau memilih sebuah subfolder.

Persisted grants are limited in number per app, and the user can revoke them in system settings or by clearing app data. Check access with getPersistedUriPermissions() Akses semua file: MANAGE_EXTERNAL_STORAGE

Semua file akses: MANAGE_EXTERNAL_STORAGE

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

This permission gives the old “read and write everything” behavior, but it is not a runtime prompt. The user must enable it on a dedicated settings screen, and Google Play reviews it manually. Accepted use cases are file managers, backup and restore, antivirus, document management, on-device file search, and a few others where broad access is the core feature. A note-taking app, a PDF viewer that opens files from Downloads, or an app that “just needs to save exports” will be rejected.

Jika Anda distribusikan di luar Play (perusahaan MDM, perangkat kiosk yang disediakan), itu adalah pilihan. Jika tidak, rancang sekitar MediaStore dan SAF.

Bagaimana plugin lainnya berintegrasi

Plugin Penyimpanan Terbatas
@capacitor/filesystem Baik dengan direktori aplikasi. Documents dan ExternalStorage sudah tidak kompatibel di Android 11+. requestPermissions() Hanya berlaku untuk direktori pada Android yang lama
@capacitor/camera Menggunakan kamera sistem dan Picker Foto, tidak ada izin penyimpanan except saveToGallery pada Android 9 dan lebih lama
@capacitor/share Berbagi melalui FileProviderpada Android 9 dan lebih lama res/xml/file_paths.xml Dapat dibagikan
@capgo/capacitor-file-sharer pada Android 9 dan lebih lama FileProvider dan MediaStore untuk save()pada Android 9 dan lebih lama
@capgo/capacitor-downloader Unduh ke penyimpanan eksternal aplikasi, kemudian gunakan save() untuk mengexport
@capgo/capacitor-file-picker Pilih berdasarkan, tidak memerlukan izin pada Android 13+
@capgo/capacitor-photo-library Manifestnya menambahkan READ_MEDIA_* untuk membaca library secara lengkap, dengan memenuhi kebijakan Play di atas. pickMedia() menggunakan pilih sistem dan tidak memerlukan izin, jadi jika itu semua yang Anda gunakan, hapus READ_MEDIA_* dengan tools:node="remove"

Di mana direktori untuk digunakan

  • Data aplikasi, pengaturan, database: Directory.Data
  • File sementara dan ekspor berbagi: Directory.Cache
  • Media yang dapat diunduh kembali besar: Directory.External pada Android, Directory.LibraryNoCloud Pada iOS
  • Sebuah file yang pengguna ingin di Downloads atau Pictures: MediaStore via FileSharer.save()
  • Sebuah file yang pengguna membawa ke dalam aplikasi: picker, kemudian salin ke Directory.Data
  • Sebuah folder yang pengguna ingin aplikasi menulis kembali: SAF tree URI dengan grant yang disimpan

Untuk akses penuh baca dan tulis API, lihat Panduan pengelolaan file Capacitor.

Migrasi aplikasi yang sudah ada

  1. Cari code Anda untuk Directory.Documents, Directory.ExternalStorage, /storage/emulated, dan requestLegacyExternalStorage.
  2. Pindahkan file yang dimiliki aplikasi ke Directory.Data atau Directory.ExternalPada pertama kali menjalankan versi baru, salin semua file lama yang masih dapat dibaca, kemudian hapus mereka.
  3. Ubah fitur "simpan ke perangkat" dengan FileSharer.save().
  4. Ganti input jalur dengan pilihan.
  5. Tambahkan android:maxSdkVersion ke penyimpanan legacy dan hapus READ_MEDIA_* kecuali Anda membutuhkannya.
  6. Jalankan tes pada perangkat Android 10, 11, 13, dan 15 atau 16, atau emulator. Sifatnya berbeda tepat pada batasan-batasan tersebut.

preserveLegacyExternalStorage="true" di <application> tag ini menjaga akses legacy untuk aplikasi yang diperbarui dari versi yang menggunakan requestLegacyExternalStoragesebelum aplikasi diinstal ulang. Ini hanya membantu selama langkah migrasi satu kali, jadi jangan bergantung padanya.

Troubleshooting

EACCES (Permission denied) menulis ke /storage/emulated/0/...anda menulis ke penyimpanan bersama secara langsung. Gunakan MediaStore atau direktori aplikasi.

File disimpan dengan Directory.Documents hilang setelah reinstall: aplikasi kehilangan kepemilikan atasnya. Gunakan MediaStore untuk file pengguna.

FileNotFoundException membaca sebuah content:// URI kemudian: izin sementara telah berakhir. Salin file ketika dipilih, atau tetapkan izin untuk URI-URI yang lebih dari satu.

peringatan Play Console tentang izin foto dan video: plugin tambahan READ_MEDIA_IMAGES atau READ_MEDIA_VIDEOHapus mereka dengan tools:node="remove" jika Anda tidak membutuhkannya.

requestPermissions() izin kembali diberikan tetapi menulis masih gagal: pada Android 11+ izin penyimpanan warisan diberikan tetapi tidak bermakna untuk penyimpanan bersama.

Sinkronkan bagian native dan web

Aturan penyimpanan adalah konfigurasi native, jadi perubahan pada manifest atau plugin memerlukan rilis penyimpanan, yang Capgo Pembangunan Dapat menghasilkan di awan. Logika file di sekitar mereka (path, nama folder, aliran ekspor) adalah JavaScript yang dapat Anda perbarui dengan update Capgo secara langsung.

Update instan untuk aplikasi Capacitor

Jika ada bug layer web yang hidup, kirimkan perbaikan melalui Capgo daripada menunggu hari-hari untuk persetujuan toko aplikasi. Pengguna mendapatkan update di latar belakang sementara perubahan native tetap dalam jalur review normal.

Terbaru dari Blog kami

Capgo memberikan Anda wawasan terbaik yang Anda butuhkan untuk membuat aplikasi seluler yang profesional.