Untuk mengambil dan mengedit foto di aplikasi Capacitor, gunakan @capacitor/camera 8.1 atau lebih baru: Camera.takePhoto() membuka kamera, editable: 'in-app' menambahkan langkah potong dan putar, dan chooseFromGallery() memilih foto yang sudah ada melalui picker sistem. Untuk layar kamera yang disesuaikan dengan kontrol sendiri, gunakan @capgo/camera-preview. Panduan ini mencakup pengambilan, pengeditan, izin, kompresi, metadata, menyimpan, dan mengunggah, dengan code untuk Capacitor 8.
Pilih alat yang tepat
| Perlu | Plugin | Mengapa |
|---|---|---|
| Ambil foto dengan antarmuka kamera sistem | @capacitor/camera takePhoto |
Tidak perlu UI khusus untuk dipelihara, berfungsi di mana saja |
| Biarkan pengguna memotong atau memutar | @capacitor/camera editable, editPhoto, editURIPhoto |
Pengeditan bawaan di dalam aplikasi di iOS dan Android |
| Pilih satu atau banyak foto atau video | @capacitor/camera chooseFromGallery |
Gunakan Picker Foto Android dan PHPicker, tanpa izin luas yang luas |
| Screen kamera yang dapat disesuaikan (overlay, frame ID kartu, panduan wajah) | @capgo/camera-preview |
Penglihatan asli yang dapat dikendalikan dari HTML dan JS |
| Akses perpustakaan seluruhnya di dalam grid sendiri | @capgo/capacitor-photo-library |
Akses perpustakaan yang dipaginasi dengan thumbnail |
| Memampatkan, mengubah ukuran atau mengonversi HEIC | @capgo/capacitor-file-compressor |
Encoder asli, menghilangkan EXIF |
| Mengunggah foto besar secara andal | @capgo/capacitor-uploader |
Mengunggah latar belakang dengan kemajuan |
Instal dan konfigurasi plugin Kamera
bun add @capacitor/camera
bunx cap sync
Pastikan Anda memiliki versi 8.1 atau lebih baru. Metode dalam panduan ini (takePhoto, chooseFromGallery, editPhoto, editURIPhoto) ditambahkan pada 8.1, dan CameraErrorCode In 8.2. Yang lebih tua getPhoto dan pickImages telah dihapus.
iOS
Tambahkan kunci-kunci ini ke ios/App/App/Info.plist. Apple menolak membangun dengan string yang tidak jelas, jadi jelaskan alasan sebenarnya:
<key>NSCameraUsageDescription</key>
<string>Take a photo for your profile or attach it to a report.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose existing photos to attach.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Save photos you take to your library.</string>
Android
Memotret dan memilih galeri tidak memerlukan izin. Plugin ini memulai aktivitas kamera sistem dan menggunakan Pemilih Foto Android pada perangkat Android 11+ yang mendukungnya, kembali ke ACTION_OPEN_DOCUMENT untuk mendapatkan pemilih yang diperbarui pada perangkat yang lebih tua dengan layanan Google Play, tambahkan ini di dalam <application> In AndroidManifest.xml. Potongan kode ini menggunakan tools nama ruang, jadi tambahkan juga xmlns:tools="http://schemas.android.com/tools" ke root <manifest> Jika elemen tersebut belum ada (projek baru hanya mengumumkan Capacitor xmlns:android):
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
android:enabled="false"
android:exported="false"
tools:ignore="MissingClass">
<intent-filter>
<action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
</intent-filter>
<meta-data android:name="photopicker_activity:0:required" android:value="" />
</service>
Hanya saveToGallery: true memerlukan izin penyimpanan, dan hanya pada versi Android lama:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
Hindari menambahkan READ_MEDIA_IMAGES kecuali tujuan utama aplikasi Anda adalah galeri atau editor. Google Play memiliki kebijakan izin foto dan video yang memerlukan aplikasi dengan kebutuhan foto sementara menggunakan picker, dan bangun yang mengumumkan izin tanpa alasan akan ditolak.
Ambil foto
import { Camera, CameraDirection, CameraErrorCode, EncodingType } from '@capacitor/camera';
export async function takePicture() {
try {
const photo = await Camera.takePhoto({
quality: 85,
targetWidth: 2048,
targetHeight: 2048,
correctOrientation: true,
encodingType: EncodingType.JPEG,
cameraDirection: CameraDirection.Rear, // iOS and web
includeMetadata: true,
});
// webPath works directly in <img src>
document.querySelector<HTMLImageElement>('#preview')!.src = photo.webPath!;
return photo;
} catch (e: any) {
if (e.code === CameraErrorCode.TakePhotoCancelled) return null;
if (e.code === CameraErrorCode.CameraPermissionDenied) {
// show a screen that explains how to enable the camera in Settings
return null;
}
throw e;
}
}
Apa yang Anda dapatkan adalah sebuah MediaResult:
uri: URI file asli native dari gambar penuh (tidak di web). Kirimkannya ke Filesystem, uploader, ataueditURIPhoto.webPath: sebuah URL yang dapat diakses oleh WebView. Gunakan untuk pratinjau daripada base64.thumbnail: sebuah thumbnail base64 native, gambar penuh di web.metadata: ukuran, format, resolusi, tanggal pembuatan dan EXIF ketikaincludeMetadatabenar.saved: apakahsaveToGalleryberhasil.
targetWidth dan targetHeight harus disetel bersama. Mereka adalah cara termurah untuk menghindari mengunggah foto 12 MP ketika Anda hanya membutuhkan gambar 2048 piksel.
Edit foto: potong dan putar
Edit setelah tangkapan
Set editable dan pengguna mendarat di editor sebelum janji diselesaikan:
const photo = await Camera.takePhoto({
quality: 85,
editable: 'in-app', // 'in-app' | 'external' | 'no'
});
'external' menghadiahkan gambar ke editor lain yang diinstal di Android (misalnya Google Photos) dan kembali ke editor di aplikasi. iOS tidak memiliki editing eksternal dan selalu menggunakan 'in-app'.
Edit foto yang sudah ada
Untuk mengedit foto yang dipilih atau yang disimpan sebelumnya, lewatkan URI-nya:
const edited = await Camera.editURIPhoto({
uri: photo.uri!,
saveToGallery: false,
includeMetadata: false,
});
Jika Anda hanya memiliki base64 (misalnya dari canvas atau respons server), gunakan editPhoto. Masukan harus berupa base64 mentah tanpa prefix: data: prefix:
const { outputImage } = await Camera.editPhoto({ inputImage: base64 });
img.src = `data:image/jpeg;base64,${outputImage}`;
dan aplikasikan perubahan di sana, atau gunakan pemotong JavaScript seperti Cropper.js untuk rasio aspek tetap seperti avatar. Ekspor canvas ( <canvas> dan aplikasikan perubahan-perubahan tersebut di sana, atau gunakan pemotong JavaScript seperti Cropper.js untuk rasio aspek tetap seperti avatar.canvas.toBlob(cb, 'image/jpeg', 0.85)Mengembalikan Anda sebuah Blob yang dapat diunggah langsung.
memberikan Anda pilih-lalu-potong dalam satu panggilan. Atur
import { Camera, MediaTypeSelection } from '@capacitor/camera';
const { results } = await Camera.chooseFromGallery({
mediaType: MediaTypeSelection.Photo,
allowMultipleSelection: true,
limit: 5, // Android 13+ and iOS
quality: 85,
targetWidth: 2048,
targetHeight: 2048,
});
const urls = results.map((r) => r.webPath);
untuk menerima video juga. allowMultipleSelection: false plus editable: 'in-app' memberi Anda pilihan lalu potong dalam satu langkah. Tetapkan mediaType: MediaTypeSelection.All Untuk foto tunggal
Pada iOS, pengguna dapat memberikan akses terbatas. pickLimitedLibraryPhotos() biarkan mereka mengubah pilihan dan getLimitedLibraryPhotos() kembali apa yang Anda lihat. Dengan picker sistem Anda jarang perlu salah satu, karena item yang dipilih selalu dapat dibaca.
Membangun grid galeri Anda sendiri
Jika aplikasi Anda menampilkan perpustakaan pengguna di dalam UI sendiri (sebuah aplikasi cetak foto, aplikasi backup), gunakan @capgo/capacitor-photo-library:
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
const { state } = await PhotoLibrary.requestAuthorization();
if (state === 'authorized' || state === 'limited') {
const { assets, hasMore } = await PhotoLibrary.getLibrary({
limit: 60,
offset: 0,
thumbnailWidth: 256,
thumbnailHeight: 256,
});
// assets[i].thumbnail?.webPath for the grid, getPhotoUrl({ id }) for full size
}
Plugin yang sama pickMedia() membuka picker sistem dan menyalin pilihan ke cache, jika Anda ingin satu plugin untuk aliran yang sama. Ingatlah kebijakan Google Play di atas sebelum meminta akses penuh ke perpustakaan di Android.
Membangun layar kamera custom
UI kamera sistem tidak dapat menampilkan kerangka ID kartu, garis-garis dokumen, atau merek Anda. @capgo/camera-preview mengrender feed kamera native dan memungkinkan Anda menarik UI di HTML:
bun add @capgo/camera-preview
bunx cap sync
import { CameraPreview } from '@capgo/camera-preview';
await CameraPreview.start({
position: 'rear',
toBack: true, // preview behind the WebView
aspectRatio: '4:3',
storeToFile: true, // capture() returns a file path instead of base64
disableAudio: true,
});
await CameraPreview.setFlashMode({ flashMode: 'auto' });
await CameraPreview.setZoom({ level: 2 });
const { value: filePath, exif } = await CameraPreview.capture({ quality: 85 });
await CameraPreview.stop();
Dengan toBack: true, preview ditempatkan di belakang WebView, sehingga halaman dan setiap elemen induk harus memiliki latar belakang transparan saat kamera aktif. Pola umum adalah camera-active kelas pada body yang mengatur background: transparent and hides the rest of the app. Add NSCameraUsageDescription di iOS, dan di Android plugin meminta CAMERA ketika layar ditutup untuk melepaskan kamera. requestPermissions()Tampilkan, kompres, dan konversi stop() ketika layar ditutup untuk melepaskan kamera.
Tampilkan, kompresi, dan konversi
Use webPath (or Capacitor.convertFileSrc(path) Untuk menampilkan gambar, hindari penggunaan base64 <img> tag untuk foto penuh ukuran: sebuah JPEG 4 MB menjadi sebuah string 5,3 MB di memori JS dan menyeberangi jembatan.
sebelum mengunggah, kecilkan gambar:
import { FileCompressor } from '@capgo/capacitor-file-compressor';
const { path } = await FileCompressor.compressImage({
path: photo.uri!,
width: 1600, // keeps aspect ratio
quality: 0.75,
mimeType: 'image/jpeg',
});
Kompresor menerima input HEIC dari iPhone, jadi panggilan yang sama mengonversi HEIC ke JPEG untuk backend yang tidak dapat membaca HEIC. Ia menulis hasilnya ke direktori sementara, jadi pindahkanlah dengan @capacitor/filesystem jika Anda perlu menjaganya.
metadata EXIF dan privasi lokasi
Yang includeMetadata: true, metadata.exif Mengandung blok EXIF foto. Foto dari galeri kamera dapat mencakup koordinat GPS. Dua aturan ini akan menjaga Anda tetap aman:
- Menghilangkan lokasi sebelum mengunggah foto yang menjadi publik (foto profil, daftar marketplace).
@capgo/capacitor-file-compressorMenghilangkan EXIF di semua platform sebagai bagian dari kompresi. - Jika Anda membutuhkan lokasi (laporan lapangan, inspeksi), baca dari EXIF sebelum kompresi dan kirim sebagai bidang terpisah. Kemudian, beritahukan di label privasi App Store dan formulir keamanan data Play. Lihat panduan manifest privasi kami Petunjuk Privasi Simpan ke galeri atau penyimpanan aplikasi
Simpan ke galeri atau penyimpanan aplikasi
saveToGallery: true pada takePhoto atau editURIPhoto menulis gambar ke rol kamera dan mengatur saved Membuat foto tersebut tetap pribadi untuk aplikasi Anda, salin foto tersebut ke penyimpanan aplikasi.
import { Filesystem, Directory } from '@capacitor/filesystem';
await Filesystem.copy({
from: photo.uri!,
to: `photos/${Date.now()}.jpg`,
toDirectory: Directory.Data,
});
File di direktori cache dapat dihapus oleh sistem operasi kapan saja, jadi jangan menyimpan referensi yang berumur panjang kepadanya.
Upload the photo
Untuk foto kecil, fetch dengan FormData adalah cukup:
const blob = await fetch(photo.webPath!).then((r) => r.blob());
const form = new FormData();
form.append('file', blob, 'photo.jpg');
await fetch('https://api.example.com/photos', { method: 'POST', body: form });
Untuk file besar atau jaringan yang tidak stabil, gunakan uploader native yang tetap berjalan ketika aplikasi di background:
import { Uploader } from '@capgo/capacitor-uploader';
const { id } = await Uploader.startUpload({
filePath: path,
serverUrl: 'https://api.example.com/photos',
method: 'POST',
mimeType: 'image/jpeg',
});
await Uploader.addListener('events', (event) => {
console.log(event.name, event.payload);
});
Contoh: alur foto profil
- Tampilkan sheet aksi dengan “Ambil foto” dan “Pilih dari galeri”.
- Panggil
takePhotoatauchooseFromGallerywitheditable: 'in-app',targetWidth: 1024,targetHeight: 1024. - Potong menjadi persegi dalam sebuah kanvas jika Anda membutuhkan rasio 1:1 yang dijamin.
- Compress dengan
quality: 0.8Mengompresi dengan - Unggah terlebih dahulu, kemudian tampilkan URL server, bukan file lokal, sehingga avatar tetap bertahan setelah penginstalan ulang.
Troubleshooting
Aplikasi mengalami crash di iOS ketika kamera dibuka.Kunci deskripsi pengguna : tidak ada Info.plist.
Aplikasi mengalami crash pada iOS ketika kamera dibuka: set correctOrientation: true (default) dan hindari membaca piksel mentah tanpa menerapkan orientasi EXIF.
Aplikasi akan restart setelah mengambil foto pada perangkat Android dengan memori rendah: Android mungkin membunuh aktivitas Anda saat kamera masih terbuka. Dengarkan appRestoredResult from @capacitor/app untuk mengembalikan hasil setelah restart.
webPath : Anda menyimpandan menggunakannya lagi setelah restart. Simpan webPath atau jalur filesystem dan konversi lagi dengan uri atau sebuah path file sistem dan konversi lagi dengan Capacitor.convertFileSrc.
Tampilan kamera menampilkan layar hitam: elemen di atas WebView masih memiliki warna latar belakang, start() Play Console menolak membangun aplikasi untuk izin foto
untuk memulihkan hasil setelah restart.remove READ_MEDIA_IMAGES dan READ_MEDIA_VIDEO kecuali Anda telah mengajukan deklarasi bahwa aplikasi Anda memerlukan akses luas.
Kirim perbaikan tanpa rilis toko
Ukuran gambar, tingkat kompresi, rasio potong, dan endpoint unggah semua berada di web code. Dengan Capgo live updates you can change them for every user without waiting for review, while native changes like new plugins go through a regular build. If you need iOS builds without a Mac, Capgo Pembangunan menangani tanda tangan dan unggah dengan Xcode 26, yang App Store Connect memerlukan sejak April 2026.