要在 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 内置应用内编辑器 |
| 选择一张或多张照片或视频 | @capacitor/camera chooseFromGallery |
使用 Android Photo Picker 和 PHPicker,无需广泛权限 |
| 自定义相机界面(叠加层、证件照框、人脸引导) | @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
拍摄和从图库选择无需权限。该插件会启动系统相机 Activity,并在支持该功能的 Android 11+ 设备上使用 Android 照片选择器,否则回退到 ACTION_OPEN_DOCUMENT 。要在装有 Google Play 服务的旧设备上获取回移的照片选择器,请在 <application> 中的 AndroidManifest.xml内添加此代码。该代码片段使用了 tools 命名空间,所以也要添加 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 的照片和视频权限政策要求只是偶尔需要照片的应用改用选择器,而没有正当理由就声明该权限的构建会被拒绝。
拍照
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 信息includeMetadata为 true 时。saved:是否saveToGallery成功。
targetWidth 以及 targetHeight 必须同时设置。这是避免在只需 2048 像素图像时上传 12 MP 照片的最经济方式。
编辑照片:裁剪和旋转
拍摄后立即编辑
设置 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);
对于单张照片, allowMultipleSelection: false 加 editable: 'in-app' 可以在一次调用中完成选择和裁剪。设置 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() 打开系统选择器并将选择内容复制到缓存中,如果你希望用一个插件处理这两种流程。在 Android 上请求完整图库访问权限前,请记住上述 Google Play 政策。
构建自定义相机界面
系统相机界面无法显示身份证框、文档轮廓或你的品牌标识。 @capgo/camera-preview 渲染原生相机画面,并允许你用 HTML 绘制 UI:
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 上添加 body 类,设置 background: transparent 并隐藏应用的其余部分。添加 NSCameraUsageDescription 在 iOS 上,而在 Android 上,该插件会请求 CAMERA 通过 requestPermissions()。始终调用 stop() 在屏幕关闭时释放相机。
显示、压缩和转换
使用 webPath (或 Capacitor.convertFileSrc(path) 用于来自其他插件的路径)来显示图片。避免在 <img> 标签里放全尺寸照片:4 MB 的 JPEG 在 JS 内存中会变成 5.3 MB 的字符串,还要经过 bridge。
上传前先压缩图片:
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 转成 JPEG,供无法读取 HEIC 的后端使用。结果会写入临时目录,请用以下命令移动它 @capacitor/filesystem (如果需要保留)。
EXIF 元数据与位置隐私
使用 includeMetadata: true, metadata.exif 包含照片的 EXIF 块。相册里的照片可能带有 GPS 坐标。记住两条规则就能避免麻烦:
- 上传会公开的照片(头像、商城商品列表)前,先移除位置信息。
@capgo/capacitor-file-compressor在所有平台上,压缩时都会移除 EXIF。 - 如果需要位置信息(现场报告、巡检),请在压缩前从 EXIF 读取,并作为单独字段发送。然后在 App Store 隐私标签和 Play 数据安全表单中披露。请参阅我们的 privacy manifest 指南 了解 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,
});
缓存目录中的文件可能随时被操作系统删除,因此不要长期保留对它们的引用。
上传照片
对于较小的照片, 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.8并移除 EXIF 信息。 - 上传后显示服务器 URL,而不是本地文件,这样头像在重装后依然保留。
故障排除
iOS 上打开相机时应用会崩溃:缺少 usage description 键,位于 Info.plist.
Android 上照片会旋转:设置 correctOrientation: true (默认值)并避免在未应用 EXIF 方向时读取原始像素。
在低内存 Android 设备上拍照后应用会重启:相机打开期间,Android 可能会杀掉你的 Activity。请监听 appRestoredResult 来自 @capacitor/app 以便在重启后恢复结果。
webPath 无法加载:你保存了 webPath 并在重启后复用它。请持久化 uri 或 Filesystem 路径,并再次使用 Capacitor.convertFileSrc.
Camera 预览显示黑屏:WebView 上方的元素仍带有背景色,或者 start() :在获得权限之前就被调用了。
Play Console 因照片权限拒绝该 build:移除 READ_MEDIA_IMAGES 以及 READ_MEDIA_VIDEO 除非您已提交声明,表明您的应用需要广泛访问权限。
无需商店发布即可推送修复
图片尺寸、压缩级别、裁剪比例和上传端点都位于您的 Web code 中。使用 Capgo 实时更新 您可以为所有用户更改这些设置,而无需等待审核;而新插件等原生更改则通过常规 build 进行。如果您需要在没有 Mac 的情况下构建 iOS 应用, Capgo 构建 它使用 Xcode 26 处理签名和上传,这是 App Store Connect 自 2026 年 4 月起的要求。