Capacitor アプリで写真を撮影・編集するには、 @capacitor/camera 8.1 以降を使用します: Camera.takePhoto() カメラを開き、 editable: 'in-app' トリミングと回転のステップを追加し、 chooseFromGallery() システムピッカーで既存の写真を選択します。独自のコントロール付きのカスタムカメラ画面が必要な場合は、 @capgo/camera-previewを使用します。本ガイドでは、撮影、編集、権限、圧縮、メタデータ、保存、アップロードについて、Capacitor 8 向けの code を扱います。
適切なツールを選ぶ
| 要件 | プラグイン | 理由 |
|---|---|---|
| システムカメラUIで写真を撮る | @capacitor/camera takePhoto |
カスタムUIのメンテナンス不要、どこでも動作 |
| ユーザーがトリミングや回転を行える | @capacitor/camera editable, editPhoto, editURIPhoto |
iOSとAndroidに組み込まれたアプリ内エディタ |
| 1枚または複数の写真・動画を選択 | @capacitor/camera chooseFromGallery |
Android Photo PickerとPHPickerを使用、幅広い権限は不要 |
| カスタムカメラ画面(オーバーレイ、IDカード枠、顔ガイド) | @capgo/camera-preview |
HTMLとJSから制御できるネイティブプレビュー |
| 独自のグリッド内でライブラリ全体を閲覧 | @capgo/capacitor-photo-library |
サムネイル付き、ページ単位でライブラリにアクセス |
| 縮小、リサイズ、またはHEIC変換 | @capgo/capacitor-file-compressor |
ネイティブエンコーダー、EXIFを除去 |
| 大きな写真を確実にアップロード | @capgo/capacitor-uploader |
進捗表示付きのネイティブバックグラウンドアップロード |
Cameraプラグインのインストールと設定
bun add @capacitor/camera
bunx cap sync
バージョン8.1以降であることを確認してください。このガイドのメソッド(takePhoto, chooseFromGallery, editPhoto, editURIPhoto)は8.1で追加され、 CameraErrorCode は8.2で追加されました。旧バージョンの getPhoto と pickImages は非推奨です。
iOSと
にこれらのキーを追加します ios/App/App/Info.plist。Appleは曖昧な文字列を含むビルドを拒否するため、実際の理由を説明してください:
<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では
撮影とギャラリーからの選択には権限が不要です。このプラグインはシステムのカメラアクティビティを起動し、対応する Android 11 以降のデバイスでは Android Photo Picker を使用し、それ以外は ACTION_OPEN_DOCUMENT にフォールバックします。Google Play services が利用可能な古いデバイスでバックポートされたピッカーを使うには、以下を追加します <application> における AndroidManifest.xml。このスニペットは tools namespace を使用しているため、 xmlns:tools="http://schemas.android.com/tools" ルート <manifest> 要素にも追加してください(まだない場合)。新しい 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>
のみ saveToGallery: true ストレージ権限が必要なのは、古い 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" />
追加は避けてください READ_MEDIA_IMAGES (アプリの主要な目的がギャラリーやエディタでない限り)。Google Play の写真と動画の権限ポリシーでは、写真を時々しか必要としないアプリには代わりにピッカーを使うよう求めています。正当な理由なく権限を宣言した build は拒否されます。
写真を撮る
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;
}
}
返されるのは MediaResult:
uri:ネイティブ環境でのフルサイズ画像のファイル URI(Web では利用不可)。Filesystem、アップローダー、またはeditURIPhoto.webPath:WebView で読み込める URL。base64 ではなくプレビューに使用します。thumbnail:ネイティブでは base64 形式のサムネイル、Web ではフルサイズ画像。metadata:サイズ、形式、解像度、作成日、および EXIF はincludeMetadatatrue の場合に含まれます。saved:saveToGallery成功したかどうか。
targetWidth と targetHeight は必ずセットで設定してください。2048 ピクセルの画像で十分なときに 12MP の写真をアップロードせずに済む、最も低コストな方法です。
写真を編集:トリミングと回転
撮影直後に編集
設定 editable promise が解決する前に、ユーザーはエディタに遷移します:
const photo = await Camera.takePhoto({
quality: 85,
editable: 'in-app', // 'in-app' | 'external' | 'no'
});
'external' Android では、インストール済みの別のエディタ(例: Google Photos)に画像を渡し、外部編集が利用できない場合はアプリ内エディタにフォールバックします。iOS には外部編集機能がないため、常に 'in-app'.
既存の写真を編集
選択した写真、または以前保存した写真を編集するには、そのURIを渡します:
const edited = await Camera.editURIPhoto({
uri: photo.uri!,
saveToGallery: false,
includeMetadata: false,
});
base64(canvasやサーバーからのレスポンスなど)しかない場合は、次を使用します: editPhoto。入力は、生のbase64である必要があります。次を付けないでください: data: プレフィックス:
const { outputImage } = await Camera.editPhoto({ inputImage: base64 });
img.src = `data:image/jpeg;base64,${outputImage}`;
どちらの方法もWebでは使えません。フィルター、ステッカー、テキスト、フリーハンド描画が必要なら、組み込みエディターでは足りません。画像を <canvas> でレンダリングしてそこで編集するか、アバターのような固定アスペクト比ならCropper.jsなどのJavaScriptクロッパーを使ってください。canvasのエクスポート(canvas.toBlob(cb, 'image/jpeg', 0.85))を使用すると、直接アップロードできるBlobが得られます。
ギャラリーから写真を選択
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);
写真1枚の場合、 allowMultipleSelection: false と editable: 'in-app' を使用すると、1回の呼び出しで選択してクロップできます。次を設定すると mediaType: MediaTypeSelection.All 動画も受け入れられます。
iOSでは、ユーザーは限定的なアクセスを許可できます。 pickLimitedLibraryPhotos() 選択内容を変更でき、 getLimitedLibraryPhotos() 表示可能な内容を返します。システムピッカーを使えば、選択したアイテムは常に読み取り可能なので、どちらもほとんど必要ありません。
独自のギャラリーグリッドを構築する
アプリ独自のUIでユーザーのライブラリを表示する場合(写真プリントアプリやバックアップアプリなど)は、次を使用します: @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
}
同じプラグインの pickMedia() 両方のフローを1つのプラグインで完結させたい場合は、システムピッカーを開いて選択内容をキャッシュにコピーします。Androidでライブラリへのフルアクセスをリクエストする前に、前述のGoogle Playポリシーに注意してください。
カスタムカメラ画面を構築する
システムのカメラUIでは、身分証の枠や書類の輪郭、自社ブランドを表示できません。 @capgo/camera-preview ネイティブのカメラフィードを描画し、UIを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();
を使用すると、 toBack: true、プレビューはWebViewの背後に配置されるため、カメラがアクティブな間は、ページとすべての親要素の背景を透明にする必要があります。よくあるパターンは camera-active class を body 設定し、 background: transparent アプリの残りを非表示にします。次に、 NSCameraUsageDescription iOS では、Android ではプラグインが CAMERA ランタイム権限をマニフェスト経由でリクエストします requestPermissions()。必ず stop() 画面を閉じるときに呼び出してカメラを解放してください。
表示、圧縮、変換
使用する webPath (または Capacitor.convertFileSrc(path) (他のプラグインからのパスなど)で画像を表示します。 <img> フルサイズの写真に base64 を使わないでください。4 MB の JPEG は JS メモリ上では 5.3 MB の文字列になり、ブリッジを跨ぐことになります。
アップロード前に画像を縮小します:
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',
});
コンプレッサーは iPhone の HEIC 入力をサポートするため、HEIC を読めないバックエンド向けに同じ呼び出しで HEIC を JPEG に変換できます。結果は一時ディレクトリに書き込まれるため、保持が必要な場合は @capacitor/filesystem で移動してください。
EXIF メタデータと位置情報のプライバシー
を使用すると、 includeMetadata: true, metadata.exif には写真の EXIF ブロックが含まれます。カメラロールからの写真には GPS 座標が含まれる可能性があります。トラブルを避けるための 2 つのルール:
- 公開される写真(プロフィール画像、マーケットプレイスの出品など)をアップロードする前に位置情報を削除します。
@capgo/capacitor-file-compressor圧縮プロセスの一環として、全プラットフォームでEXIFを削除します。 - 位置情報が必要な場合(現場報告や点検など)は、圧縮前にEXIFから読み取り、別のフィールドとして送信してください。その後、App StoreのプライバシーラベルとPlayのデータセーフティフォームで開示してください。詳しくは プライバシーマニフェストガイド (iOS側)をご覧ください。
ギャラリーまたはアプリのストレージに保存
saveToGallery: true で takePhoto または editURIPhoto 画像をカメラロールに書き込み、結果に saved 設定します。写真をアプリ内だけに保存するには、アプリのストレージにコピーしてください:
import { Filesystem, Directory } from '@capacitor/filesystem';
await Filesystem.copy({
from: photo.uri!,
to: `photos/${Date.now()}.jpg`,
toDirectory: Directory.Data,
});
キャッシュディレクトリ内のファイルはOSによっていつでも削除される可能性があるため、長期的な参照を保持しないでください。
写真をアップロードする
小さい写真の場合、 fetch を使用して FormData で十分です:
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 });
大きいファイルやネットワークが不安定な場合は、アプリがバックグラウンドに回っても処理を継続できるネイティブアップローダーを使用してください:
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);
});
例:プロフィール写真の設定フロー
- 「写真を撮る」と「ライブラリから選択」を含むアクションシートを表示します。
- 呼び出す
takePhotoまたはchooseFromGalleryを使用してeditable: 'in-app',targetWidth: 1024,targetHeight: 1024. - 1:1 の比率を確実にしたいなら、canvas で正方形にクロップしてください。
- で圧縮し
quality: 0.8EXIF を削除します。 - アップロードしたら、ローカルファイルではなくサーバー URL を表示し、再インストールしてもアバターが残るようにします。
トラブルシューティング
iOSでカメラを開いたときにアプリがクラッシュする:に使用説明キーが欠落しています Info.plist.
Androidで写真が回転している:設定 correctOrientation: true (デフォルト)を使用し、EXIF方向を適用せずに生ピクセルを読み取らないようにしてください。
メモリ不足のAndroidデバイスで写真を撮影した後にアプリが再起動する:カメラが開いている間にAndroidがアクティビティを終了する可能性があります。 appRestoredResult と @capacitor/app を監視して、再起動後に結果を復元してください。
webPath が読み込まれません:保存した webPath 再起動後も再利用できるように、永続化します uri または Filesystem パスを受け取り、再度変換します Capacitor.convertFileSrc.
カメラプレビューが黒画面になる: WebView 上の要素に背景色が設定されているか、 start() が権限の付与前に呼び出されました。
Play Console が写真権限を理由にビルドを拒否する: 削除します READ_MEDIA_IMAGES と READ_MEDIA_VIDEO アプリが広範なアクセスを必要とする旨の申告を行っていない限り。
ストアリリースなしで修正を配信
画像サイズ、圧縮レベル、トリミング比率、アップロードエンドポイントはすべて Web の code にあります。 Capgo Live Updates レビューを待たずにユーザーごとに設定を変更でき、新しいプラグインなどのネイティブ変更は通常のビルドで反映されます。MacなしでiOSビルドが必要な場合は、 Capgo のビルド Xcode 26で署名とアップロードを処理します。App Store Connectは2026年4月以降これを必須としています。