恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
expo-image-picker 完全指南:从系统相册/相机选择图片与视频的跨平台方案
首页
资讯中心
/
expo-image-picker 完全指南:从系统相册/相机选择图片与视频的跨平台方案
expo-image-picker 完全指南:从系统相册/相机选择图片与视频的跨平台方案
发布时间:2026/9/10 3:45:10
expo-image-picker 完全指南从系统相册/相机选择图片与视频的跨平台方案【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo本文以packages/expo-image-picker官方 README 为核心骨架结合该包在仓库内的 TypeScript API、iOS/Android 原生实现与配置插件源码系统讲解 expo-image-picker 的安装配置、权限声明、Config Plugin 参数、核心 API 与底层工作流程帮助你在一篇文章内掌握在 Expo 与纯 React Native 项目中使用系统级图片/视频选择器的完整实战方案。expo-image-picker是 Expo SDK 中用于访问系统 UI 的模块它调用手机系统自带的界面让用户从系统相册Photo Library / Camera Roll中选择图片和视频或直接调用相机拍照/录像。它天然运行在 Android、iOS 与 Web 三大平台之上是构建上传头像、发布图文、拍摄视频等通用功能的标准选择。本文将依次覆盖安装方式托管与 bare 工程、双平台权限配置、Config Plugin 的完整参数说明、核心 API 与数据类型、以及这些 API 背后的原生实现原理Android Activity Result 契约与 iOS PHPicker/UIKit 选择器。一、安装托管 Expo 项目与 bare React Native 项目1.1 托管ManagedExpo 项目对于使用 Expo Go 或 EAS 构建的托管项目直接使用npx expo install安装即可它会根据当前 SDK 版本自动选择兼容的包版本npx expo install expo-image-picker托管项目中无需手动修改原生工程文件权限与原生配置统一通过 app.json / app.config.js 中的 Config Plugin 完成见第三节。1.2 bare React Native 项目在纯 React Nativebare项目中需要先确保已经安装并配置了 expo 模块体系即安装了expo核心包然后同样通过npx expo install添加依赖npx expo install expo-image-picker安装 npm 包后还需要手动完成 Android / iOS 的原生权限声明见下文。二、原生权限配置bare 项目必读本节仅适用于纯 React Nativebare工程。托管项目由 Config Plugin 自动处理。2.1 Android自动添加的三项权限该包在 AndroidManifest.xml 中自动声明了CAMERA、READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE三个权限用于直接从相机拍照和从系统相册选择两条路径!-- Added permissions -- uses-permission android:nameandroid.permission.CAMERA / uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE / uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE /仓库源码细节Android 清单中这两个存储权限带有android:maxSdkVersion32限制因为 Android 13API 33代号 Tiramisu起存储权限模型改为READ_MEDIA_*系列Android 12LAPI 32以下仍需传统的外部存储权限。同时清单还声明了queries中的IMAGE_CAPTURE/ACTION_VIDEO_CAPTUREintent用于在 Android 11 上探测可用的相机应用。2.2 iOSInfo.plist 三项用途描述iOS 必须在Info.plist中声明以下三个键否则系统会在运行时强制终止应用keyNSPhotoLibraryUsageDescription/key stringGive $(PRODUCT_NAME) permission to save photos/string keyNSCameraUsageDescription/key stringGive $(PRODUCT_NAME) permission to access your camera/string keyNSMicrophoneUsageDescription/key stringGive $(PRODUCT_NAME) permission to use your microphone/string其中NSMicrophoneUsageDescription仅在录制视频时使用拍照不需要麦克风。2.3 安装 CocoaPods 依赖完成上述配置后在 iOS 工程目录执行npx pod-install将原生模块链接进 iOS 工程。三、Config Plugin托管项目的原生配置入口Config Plugin 是 expo-image-picker 在托管/预构建prebuild流程中自动应用原生配置的机制。在 EAS Build 中该插件会被自动应用只有需要传入额外自定义参数时才需要显式声明。3.1 基本用法安装 npm 包后在app.json或app.config.js的plugins数组中添加{ expo: { plugins: [expo-image-picker] } }3.2 插件参数Props完整说明插件提供以下属性用于自定义。每次修改 props 或 plugins 后都需要重新 build必要时先 prebuild原生应用若不传任何额外属性将使用默认值。属性类型默认值说明photosPermissionstring \| falseAllow $(PRODUCT_NAME) to access your photos设置 iOSNSPhotoLibraryUsageDescription权限文案。设为false会跳过 iOS 上的该权限但不会跳过 Android 端权限cameraPermissionstring \| falseAllow $(PRODUCT_NAME) to access your camera设置 iOSNSCameraUsageDescription权限文案。设为false会跳过 iOS 上的该权限但不会跳过 Android 端权限microphonePermissionstring \| falseAllow $(PRODUCT_NAME) to access your microphone设置 iOSNSMicrophoneUsageDescription权限文案。设为false会跳过 iOS 权限同时会移除 Android 的android.permission.RECORD_AUDIO权限此外插件还支持 Android 端裁剪界面crop UI的颜色自定义属性类型说明colors.cropToolbarColorstringhex裁剪工具栏背景色colors.cropToolbarIconColorstringhex裁剪工具栏图标颜色colors.cropToolbarActionTextColorstringhex裁剪工具栏操作文字颜色colors.cropBackButtonIconColorstringhex裁剪返回按钮图标颜色colors.cropBackgroundColorstringhex裁剪界面背景色dark.colorsImagePickerColors深色模式下覆盖的颜色配置这些颜色最终写入 Android 的colors.xml与values-night/colors.xml资源文件对应实现见 withImagePicker.ts 中的setImagePickerColors。3.3 配置示例{ expo: { plugins: [ [ expo-image-picker, { photosPermission: custom photos permission, cameraPermission: Allow $(PRODUCT_NAME) to open the camera, //: Disables the microphone permission, microphonePermission: false } ] ] } }3.4 插件源码行为解读从 withImagePicker.ts 的实现可以看到三条关键逻辑iOS 权限文案通过IOSConfig.Permissions.createPermissionsPlugin将三个属性分别写入NSPhotoLibraryUsageDescription、NSCameraUsageDescription、NSMicrophoneUsageDescription。Android 麦克风权限仅当microphonePermission ! false时才通过AndroidConfig.Permissions.withPermissions追加android.permission.RECORD_AUDIO。权限阻断当microphonePermission false或cameraPermission false时会调用withBlockedPermissions显式阻断对应 Android 权限防止其他库把它们加回来存储权限不在此列因为它们被大量其他功能共用见源码注释。四、核心 API从调用到返回API 定义位于 ImagePicker.ts类型定义位于 ImagePicker.types.ts。4.1 两个主入口launchImageLibraryAsync(options)调起系统相册界面选择图片或视频。import * as ImagePicker from expo-image-picker; const result await ImagePicker.launchImageLibraryAsync({ mediaTypes: [images, videos], allowsEditing: true, aspect: [4, 3], quality: 1, }); if (!result.canceled) { console.log(result.assets[0].uri); }launchCameraAsync(options)调起系统相机界面拍照或录像需要相机权限。const result await ImagePicker.launchCameraAsync({ cameraType: ImagePicker.CameraType.front, videoMaxDuration: 15, });两点平台注意事项源码 JSDoc 已明确Web 端系统 UI 只能在用户激活如点击 Button后立即调用在componentDidMount等时机调用会被浏览器拦截同时由于浏览器平台限制Web 端不会返回canceled事件。Android 端系统可能在使用选择器时销毁MainActivity导致结果丢失需要配合getPendingResultAsync()恢复数据见 4.3。4.2 返回结果结构两个入口都返回ImagePickerResult它是成功与取消的联合类型type ImagePickerResult ImagePickerSuccessResult | ImagePickerCanceledResult; // 成功canceled 为 falseassets 为数组可为多选 { canceled: false, assets: ImagePickerAsset[] } // 取消canceled 为 trueassets 为 null { canceled: true, assets: null }ImagePickerAsset关键字段字段类型说明uristring本地文件 URI可直接作为Image的sourceassetIdstring \| null媒体库中的唯一 IDAndroid/iOS可用于 expo-media-library 管理width/heightnumber媒体尺寸系统未提供时为 0typeimage \| video \| livePhoto \| pairedVideo \| null资产类型livePhoto 仅 iOSfileNamestring \| null推荐保存文件名fileSizenumber文件大小字节exifRecordstring, any \| null开启exif选项后返回 EXIF 数据Android/iOSiOS 拍照场景不含 GPS 标签base64string \| null开启base64选项后返回 JPEG Base64 字符串durationnumber \| null视频时长毫秒非视频为 nullmimeTypestringMIME 类型pairedVideoAssetImagePickerAsset \| null与 Live Photo 配对的视频iOSfileFileWeb 端 File 对象可用 FormData 上传Base64 数据可直接拼接成 data URI 使用Image source{{ uri: data:image/jpeg;base64, asset.base64 }} style{{ width: 200, height: 200 }} /4.3 权限相关 APIAPI说明getCameraPermissionsAsync()查询相机权限状态requestCameraPermissionsAsync()请求相机权限Web 端无操作getMediaLibraryPermissionsAsync(writeOnly false)查询相册权限writeOnly为 true 时仅请求写入权限requestMediaLibraryPermissionsAsync(writeOnly false)请求相册权限useCameraPermissions()React Hook封装查询与请求const [status, requestPermission] ImagePicker.useCameraPermissions()useMediaLibraryPermissions()相册版 Hook支持{ writeOnly?: boolean }参数MediaLibraryPermissionResponse在标准PermissionResponse基础上扩展了accessPrivileges字段all完全访问、limited仅所选照片Android API 34 / iOS 14、none拒绝或未决定。4.4 getPendingResultAsync应对 Activity 销毁Android 系统有时会在选择器返回前销毁MainActivity导致已选数据丢失。此时可调用getPendingResultAsync()恢复Android若选择器成功完成返回与launchImageLibraryAsync/launchCameraAsync相同类型的对象否则返回ImagePickerErrorResult含code、message、exception。其他平台始终返回null。可以用开发者选项中的 Dont keep activities 来验证此功能。从源码看Android 侧通过handleResultUponActivityDestruction将成功结果暂存到pendingMediaPickingResultgetPendingResultAsync取回后置空见 ImagePickerModule.kt。4.5 参数校验launchImageLibraryAsync与launchCameraAsync在调用原生模块前会经过validateOptions校验ImagePicker.tsaspect的[x, y]必须均为正数否则抛ERR_INVALID_ARGUMENTquality必须在 01 之间videoMaxDuration必须为非负数。五、ImagePickerOptions全部参数详解ImagePickerOptionsImagePicker.types.ts是控制选择行为的核心对象参数类型默认值平台说明mediaTypesMediaType \| MediaType[] \| MediaTypeOptionsimages全平台可选媒体类型。新版推荐数组写法images、videos、livePhotosiOS onlylivePhotos在 Android/Web 被忽略allowsEditingbooleanfalseAndroid/iOS选择后是否进入编辑界面。Android 可裁剪旋转iOS 仅裁剪且裁剪框恒为正方形与allowsMultipleSelection互斥iOS 裁剪.bmp会转为.pngaspect[number, number]—Android编辑时保持的宽高比如[4, 3]shaperectangle \| ovalrectangleAndroid裁剪区域形状qualitynumber1.0Android/iOS压缩质量 01。iOS 选择.bmp/.png时忽略exifbooleanfalseAndroid/iOS是否返回 EXIF 数据base64booleanfalse全平台是否返回 Base64 数据allowsMultipleSelectionbooleanfalseAndroid/iOS 14/Web是否允许多选开启后allowsEditing被忽略selectionLimitnumber0Android/iOS 14多选上限0表示系统支持的最大值orderedSelectionbooleanfalseiOS 15是否显示选中序号角标并按选择顺序返回defaultTabphotos \| albumsphotosAndroid选择器默认打开的页签videoMaxDurationnumber0不限全平台录像最大时长秒。iOS 开启allowsEditing时上限自动限制为 10 分钟Web 无效果videoQualityUIImagePickerControllerQualityTypeHighiOS录像质量videoExportPresetVideoExportPresetPassthroughiOS 11视频压缩预设已废弃改用 Apple 文档推荐方案presentationStyleUIImagePickerPresentationStyleAutomaticiOS选择器的模态展示样式fullScreen / pageSheet / formSheet 等cameraTypeback \| frontback全平台前后摄像头Android 行为取决于系统相机应用preferredAssetRepresentationModeautomatic \| compatible \| currentAutomaticiOS 14PHPicker 资源表示模式legacybooleanfalseAndroid使用旧版选择器允许选择相册之外的媒体shouldDownloadFromNetworkbooleanfalseiOS是否允许从 iCloud 等远程源直接读取资源Live Photo 与 Passthrough 视频场景5.1 mediaTypes 的兼容处理旧的MediaTypeOptions枚举All/Videos/Images自 SDK 52 起已被弃用但仍可用。源码 utils.ts 中的parseMediaTypes会在检测到旧枚举时打印弃用警告并将其映射为新的数组形式All → [images, videos]字符串会被自动包装为单元素数组。5.2 参数在双端的落地Android选项定义在 ImagePickerOptions.kt使用OptimizedRecord与Field注解声明通过FloatRange(from 0.0, to 1.0)等注解在原生侧再次校验范围。JSMediaTypes会被归一化为IMAGES/VIDEOS/ALL三种MediaTypes并决定相机 intentACTION_IMAGE_CAPTURE/ACTION_VIDEO_CAPTURE与输出文件扩展名.jpeg/.mp4。iOS选项定义在 ImagePickerOptions.swiftlaunchImageLibraryAsync在未开启编辑且非相机来源时走PHPickerViewController多选流程否则走UIImagePickerController旧版流程见 ImagePickerModule.swift 的launchImagePicker分支。六、底层原理一次选择请求如何完成6.1 AndroidActivity Result 契约 临时文件Android 端采用 JetpackregisterForActivityResult契约模型注册了CameraContract、ImageLibraryContract、CropImageContract三个契约ImagePickerModule.ktlaunchCameraAsync先检查目标 Activity 是否可用ensureTargetActivityIsAvailable再请求相机权限API 29 以下同时要求存储权限然后在缓存目录创建临时文件并通过 FileProvider 转成 content URI交给系统相机应用写入。launchImageLibraryAsync直接通过系统 Photo Picker 选择。若allowsEditing为 true 且单选图片会自动串联CropImageContract进入裁剪流程ExpoCropImageActivity。结果统一交给MediaHandler.readExtras处理根据选项决定走压缩导出CompressionImageExporter、原始导出RawImageExporter还是尺寸导出DimensionsExporter并填充 EXIF / Base64 等附加字段。选择器打开期间模块用isPickerOpen标志防止并发调用重复调用会直接返回canceled: true。6.2 iOSPHPicker 与 UIImagePickerController 双路线iOS 端按场景分流ImagePickerModule.swift多选/普通相册选择使用PHPickerConfigurationPHPickerViewController支持selectionLimit、filter媒体类型过滤、preferredAssetRepresentationMode、orderedSelection等现代特性。相机拍照 / 开启编辑回退到UIImagePickerController支持cameraDevice前后摄、videoExportPreset、videoQuality、videoMaximumDuration、allowsEditing等 UIKit 原生选项。相机录制场景会检查NSMicrophoneUsageDescription是否存在checkMicrophonePermissions缺失则抛异常。iPad 上会额外配置 popover 的sourceRect/sourceView锚点。结果通过ImagePickerHandler回调didPickMedia/didPickMultipleMedia/didCancelPicking交给MediaHandler异步处理最终 resolve 给 JS Promise。整个选择过程的状态被封装在PickingContext中避免并发污染。七、常见场景实践7.1 选择单张图片并上传const pickImage async () { const result await ImagePicker.launchImageLibraryAsync({ mediaTypes: [images], allowsEditing: true, aspect: [1, 1], quality: 0.8, }); if (!result.canceled) { // result.assets[0].uri 可直接用于 Image 预览 // Web 端可用 result.assets[0].file 配合 FormData 上传 } };7.2 多选图片iOS 14 / Android / Webconst result await ImagePicker.launchImageLibraryAsync({ mediaTypes: [images], allowsMultipleSelection: true, selectionLimit: 9, // 0 表示系统上限 orderedSelection: true, // iOS 15 按选择顺序返回 });注意allowsMultipleSelection与allowsEditing互斥同时开启时源码会打印警告且编辑选项被忽略见 ImagePicker.ts。7.3 拍摄视频并限制时长const result await ImagePicker.launchCameraAsync({ mediaTypes: [videos], videoMaxDuration: 30, cameraType: ImagePicker.CameraType.back, });7.4 选前请求权限const permission await ImagePicker.requestCameraPermissionsAsync(); if (permission.granted) { await ImagePicker.launchCameraAsync(); }或使用 Hook 形式const [status, requestPermission] ImagePicker.useCameraPermissions();八、Animated GIF 与 Live Photo 的平台差异Android 的 GIF 支持若所选图片为动态 GIF仅当quality显式设为1.0且allowsEditing为false时结果仍为 GIF否则压缩/裁剪会把首帧输出为 PNG。iOS 的 GIF 支持质量和裁剪均受支持。Live PhotoiOS onlymediaTypes中包含livePhotos且选中 Live Photo 时返回的图像保持原始质量不受quality影响同时pairedVideoAsset携带配对视频开启allowsEditing时该类型会被忽略Android/Web 上该类型无效。九、深入仓库继续探索API 源码ImagePicker.ts、ImagePicker.types.ts、utils.ts配置插件plugin/src/withImagePicker.ts、plugin/src/index.tsAndroid 实现ImagePickerModule.kt、ImagePickerOptions.kt、AndroidManifest.xmliOS 实现ImagePickerModule.swift、ImagePickerOptions.swift、ImagePickerHandler.swift测试用例ImagePicker-test.native.ts、withImagePicker-test.ts、ImageUtilsBase64Tests.swift变更记录CHANGELOG.md借助这套 API 与配置体系你可以用同一份代码在 Android、iOS、Web 三端获得与系统体验一致的选择/拍摄能力并通过对 Config Plugin 参数与原生契约流程的理解精准掌控权限文案、压缩质量与编辑交互等细节。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考