Per prendere e modificare foto in un'app Capacitor, utilizza @capacitor/camera 8.1 o successivi: Camera.takePhoto() apre la fotocamera, editable: 'in-app' aggiunge un passaggio di taglio e rotazione, e chooseFromGallery() seleziona foto esistenti attraverso il picker del sistema. Per una schermata della fotocamera personalizzata con i propri controlli, utilizza @capgo/camera-previewQuesta guida copre la cattura, l'editing, i permessi, la compressione, i metadati, il salvataggio e l'upload, con code per Capacitor 8.
Scegli l'attrezzatura giusta
| Need | Plugin | Perché |
|---|---|---|
| Take a photo with the system camera UI | @capacitor/camera takePhoto |
Nessun UI personalizzato da mantenere, funziona ovunque |
| Lascia all'utente tagliare o ruotare | @capacitor/camera editable, editPhoto, editURIPhoto |
Editor integrato all'interno dell'app per iOS e Android |
| Seleziona una o più foto o video | @capacitor/camera chooseFromGallery |
Utilizza il Photo Picker Android e PHPicker, senza autorizzazione generale |
| Custom camera screen (overlays, ID card frame, face guide) | @capgo/camera-preview |
Anteprima nativa che puoi controllare da HTML e JS |
| Esplora tutta la libreria all'interno della tua griglia | @capgo/capacitor-photo-library |
Accesso alla libreria paginato con miniature |
| Riduci, ridimensiona o converti HEIC | @capgo/capacitor-file-compressor |
Encoder nativo, elimina EXIF |
| Carica foto grandi in modo affidabile | @capgo/capacitor-uploader |
Carica in background nativa con progresso |
Installa e configura il plugin della fotocamera
bun add @capacitor/camera
bunx cap sync
Assicurati di avere la versione 8.1 o successiva. I metodi di questa guida (takePhoto, chooseFromGallery, editPhoto, editURIPhoto) sono stati aggiunti in 8.1, e CameraErrorCode In 8.2. Le più vecchie getPhoto E pickImages Sono obsolete.
IOS
Aggiungi questi token ai ios/App/App/Info.plist. Apple rifiuta i build con stringhe vaghe, quindi spiega il motivo reale:
<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
Cattura e selezione della galleria non richiedono alcuna autorizzazione. Il plugin avvia l'attività della fotocamera del sistema e utilizza il picker di foto di Android sulle dispositivi Android 11+ che lo supportano, ricadendo su ACTION_OPEN_DOCUMENT altrove. Per ottenere il picker retroportato sui dispositivi più vecchi con Google Play services, aggiungi questo all'interno di <application> In AndroidManifest.xml. Il frammento utilizza lo tools namespace, quindi aggiungi anche xmlns:tools="http://schemas.android.com/tools" alla radice <manifest> elemento se non è ancora presente (un nuovo progetto Capacitor dichiara solo 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>
Solo saveToGallery: true richiede permessi di archiviazione e solo sulle vecchie versioni di Android:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
Evita di aggiungere READ_MEDIA_IMAGES se il tuo scopo principale dell'app è una galleria o un editor. La politica di Google Play sui permessi di foto e video richiede agli app che hanno bisogno di foto occasionali di utilizzare il picker, e gli edifici che dichiarano la permessi senza giustificazione vengono rifiutati.
Prendi una 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;
}
}
Ciò che ottieni di ritorno è un MediaResult:
uri: URI di file nativo dell'immagine completa (non disponibile online). Passalo a Filesystem, un caricatore, oeditURIPhoto.webPath: una URL che il WebView può caricare. Utilizzalo per le anteprime al posto di base64.thumbnail: una miniatura in base64 nativa, l'immagine completa online.metadata: dimensione, formato, risoluzione, data di creazione e EXIF quandoincludeMetadataè vero.saved: sesaveToGalleryè riuscito.
targetWidth e targetHeight devono essere impostati insieme. Sono il modo più economico per evitare di caricare foto 12 MP quando ne serve solo un'immagine di 2048 pixel.
Modifica la foto: taglia e ruota
Modifica subito dopo la cattura
Imposta editable e l'utente finisce in un editor prima che la promessa si risolva:
const photo = await Camera.takePhoto({
quality: 85,
editable: 'in-app', // 'in-app' | 'external' | 'no'
});
'external' passa l'immagine a un altro editor installato su Android (ad esempio Google Photos) e ricade sullo strumento di editing in-app. L'iOS non ha editing esterni e utilizza sempre 'in-app'.
Modifica una foto esistente
Per modificare una foto selezionata o una che hai salvato precedentemente, passa la sua URI:
const edited = await Camera.editURIPhoto({
uri: photo.uri!,
saveToGallery: false,
includeMetadata: false,
});
If non hai solo base64 (ad esempio da un canvas o una risposta del server), utilizza editPhoto. L'input deve essere base64 raw senza il data: prefix:
const { outputImage } = await Camera.editPhoto({ inputImage: base64 });
img.src = `data:image/jpeg;base64,${outputImage}`;
Nessuna delle due opzioni è disponibile nel web. Se hai bisogno di filtri, sticker, testo o disegno libero, l'editor integrato non è sufficiente. Rendere l'immagine in un <canvas> e applica le modifiche lì, o utilizza un taglia e incolla JavaScript come Cropper.js per rapporti di aspetto fissi come avatar. Un esportazione del canvas (canvas.toBlob(cb, 'image/jpeg', 0.85)) ti da un Blob che puoi caricare direttamente.
Scegli foto dalla galleria
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);
Scegli una foto allowMultipleSelection: false e editable: 'in-app' ti da pick-then-crop in un'unica chiamata. Imposta mediaType: MediaTypeSelection.All per accettare anche i video.
Gli utenti possono concedere accesso limitato su iOS. pickLimitedLibraryPhotos() lieti di cambiare la selezione e getLimitedLibraryPhotos() ritorna ciò che puoi vedere. Con il picker del sistema, di rado hai bisogno di entrambi, perché gli elementi selezionati sono sempre leggibili.
Costruire la tua griglia di galleria
Se il tuo app mostra la libreria dell'utente all'interno della tua UI (una app di stampa di foto, un'app di backup), utilizza @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
}
Lo stesso plugin pickMedia() apre il picker del sistema e copia la selezione nella cache, se desideri un plugin per entrambi i flussi. Ricorda la politica di Google Play sopra prima di richiedere l'accesso completo alla libreria su Android.
Costruisci una schermata di camera personalizzata
La UI della fotocamera non può visualizzare un rettangolo per la carta d'identità, un contorno del documento o il tuo marchio. @capgo/camera-preview rende la feed della camera nativa e ti consente di disegnare la UI in 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();
Con toBack: true, il preview si trova dietro il WebView, quindi la pagina e ogni elemento padre devono avere un background trasparente mentre la camera è attiva. Un modello comune è un camera-active class su body che configura background: transparent e nasconde il resto dell'app. Aggiungi NSCameraUsageDescription su iOS, e su Android il plugin richiede la CAMERA permeabilità runtime attraverso requestPermissions(). Chiamalo sempre stop() quando la schermata si chiude per rilasciare la fotocamera.
Visualizza, comprimi e converti
Usa webPath (o Capacitor.convertFileSrc(path) per mostrare immagini. Evita l'uso di base64 <img> tagli per foto a dimensioni intere: un JPEG da 4 MB diventa una stringa da 5,3 MB in memoria JS e supera il ponte.
Prima di caricare, riduci l'immagine:
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',
});
The compressore accetta l'input HEIC dagli iPhone, quindi lo stesso chiamata converte HEIC in JPEG per i backend che non possono leggere HEIC. E' scritto il risultato in un directory temporanea, quindi spostalo con @capacitor/filesystem se hai bisogno di mantenerlo.
L'EXIF e la privacy della posizione
With includeMetadata: true, metadata.exif Contiene il blocco EXIF della foto. Le foto dal rullino della fotocamera possono includere coordinate GPS. Due regole ti tengono fuori dai guai:
- Rimuovi la posizione prima di caricare le foto che diventeranno pubbliche (immagini del profilo, elenchi del mercato).
@capgo/capacitor-file-compressorelimina EXIF su tutte le piattaforme come parte della compressione. - If you need location (field reports, inspections), read it from EXIF before compressing and send it as a separate field. Then disclose it in your App Store privacy label and Play data safety form. See our per il lato iOS. Salva nella galleria o nel storage dell'app
Salva nella galleria o archiviazione dell'app
saveToGallery: true on takePhoto o editURIPhoto scrive l'immagine nella cartella della fotocamera e imposta saved per mantenere una foto privata per il tuo app, copia la foto nella memoria dell'app:
import { Filesystem, Directory } from '@capacitor/filesystem';
await Filesystem.copy({
from: photo.uri!,
to: `photos/${Date.now()}.jpg`,
toDirectory: Directory.Data,
});
i file nella cartella di cache possono essere rimossi dall'System operativo in qualsiasi momento, quindi non memorizzare riferimenti a lungo termine ad essi.
Carica la foto
Per foto piccole fetch con FormData è sufficiente:
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 });
Per file grandi o reti instabili, utilizza un caricatore nativo che continua a funzionare anche quando l'app è in 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);
});
Esempio: flusso di immagine del profilo
- Mostra un pannello di azioni con “Scatta foto” e “Scegli dalla libreria”.
- Chiamare
takePhotoochooseFromGalleryconeditable: 'in-app',targetWidth: 1024,targetHeight: 1024. - Seleziona una proporzione quadrata in un canvas se hai bisogno di una ratio 1:1 garantita.
- Comprimi con
quality: 0.8e rimuovi EXIF. - Incarica, quindi mostra l'URL del server, non il file locale, in modo che l'avatar sopravviva ai reinstalli.
Risolvere i problemi
La app si blocca su iOS quando si apre la fotocamera: manca una chiave di descrizione di utilizzo in Info.plist.
La foto è ruotata su Android: impostare correctOrientation: true (la impostazione predefinita) e evita di leggere i pixel raw senza applicare l'orientamento EXIF.
L'applicazione si riavvia dopo aver scattato una foto su dispositivi Android con memoria bassa: l'Android può uccidere la tua attività mentre la fotocamera è aperta. Ascolta appRestoredResult da @capacitor/app per recuperare il risultato dopo il riavvio.
webPath non carica: hai salvato il webPath e l'hai riutilizzato dopo un riavvio. Persisti il uri o un percorso di sistema di file e convertilo nuovamente con Capacitor.convertFileSrc.
La preview della fotocamera mostra uno schermo nero: un elemento sopra il WebView ha ancora un colore di sfondo, o start() era stato chiamato prima che fosse stato concesso il permesso.
Il Console di Gioco rifiuta l'elaborazione per le autorizzazioni alle foto.rimuovi READ_MEDIA_IMAGES e READ_MEDIA_VIDEO se non hai dichiarato che la tua app richiede un accesso ampio.
Invia correzioni senza rilascio del negozio
Dimensioni immagini, livelli di compressione, rapporti di taglio e endpoint di caricamento sono tutti presenti nella tua web code. Con Capgo aggiornamenti in tempo reale puoi cambiarli per ogni utente senza aspettare la revisione, mentre le modifiche native come nuovi plugin vanno attraverso un build regolare. Se hai bisogno di build iOS senza un Mac Capgo Build gestisce la firma e il caricamento con Xcode 26, che App Store Connect richiede dal 2026.