expo-image-picker 更新日志深度解析Expo 图片选择器演进全指南【免费下载链接】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/CHANGELOG.md 为骨架结合该模块的 TypeScript 源码src/、Kotlin 实现android/、Swift 实现ios/与配置插件plugin/系统梳理expo-image-picker从 SDK 8 到 SDK 57 的关键演进、API 变化与底层原理帮助开发者理解当前版本每个选项的来源与正确用法。一、模块定位Expo 通用原生应用的图片选择能力expo-image-picker是 Expo 生态中负责从系统 UI 选择图片/视频与调用相机拍照/录像的官方模块当前版本为 57.0.7见 package.json。其包描述为Provides access to the systems UI for selecting images and videos from the phones library or taking a photo with the camera.它运行于 Android、iOS 与 Web 三大平台是构建上传头像、拍摄照片、选择视频等功能的通用方案。该模块的核心设计原则是尽可能复用系统原生的选择器 UI而非自绘界面iOS 14 默认使用PHPickerViewController相册与UIImagePickerController相机含 Legacy 路径Android 优先使用系统 Photo PickerActivityResultContracts.PickVisualMedia与系统相机 IntentACTION_IMAGE_CAPTURE/ACTION_VIDEO_CAPTUREWeb 使用input typefile与input typefile capture。下文将沿 CHANGELOG 的时间线把每一类重要变更还原到源码中讲解。二、版本时间线总览SDK 区间版本号关键主题SDK 8 ~ 98.0 ~ 9.2权限声明、base64 行为、Web 支持SDK 10 ~ 1210.0 ~ 12.0config plugin 诞生、iOS 13 起、结果对象重构SDK 13 ~ 1413.0 ~ 14.7Swift/Kotlin 重写、Android Photo Picker、多选SDK 15 ~ 1615.0 ~ 16.1权限可禁用、quality默认值变更、iOS fast-pathSDK 175717.0 ~ 57.0.7裁剪 UI 定制、web blob URI、57 系列维护注CHANGELOG 中版本号存在一次跳跃——从55.x直接进入17.0.x随后升至56/57。这是 Expo 仓库对内部模块版本策略调整的结果开发者应关注的是同一 SDK 周期内的功能语义而非数字上的线性关系。三、SDK 8 ~ 9 时代权限与基础能力的奠基3.1 Android 权限自动声明在 9.0.0 中模块开始在AndroidManifest.xml中声明相机与外部存储权限。这一设计延续至今当前 android/src/main/AndroidManifest.xml 中声明uses-permission android:nameandroid.permission.CAMERA / uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE android:maxSdkVersion32 / uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE android:maxSdkVersion32 /注意maxSdkVersion32的细节从 Android 13API 33起READ/WRITE_EXTERNAL_STORAGE已不再适用于媒体库访问取而代之的是 16.0.0 中删除的READ_MEDIA_IMAGES/READ_MEDIA_VIDEO权限Android 13 的细粒度权限。在 55.0.0 中又为这两个权限补充了maxSdkVersion注解。3.2 Web 平台的行为约束CHANGELOG 中反复出现的 Web 修复如 10.2.0 的 base64 返回、15.0.0 的取消对话框结果都指向同一个事实Web 端必须由用户激活点击按钮后调用launchCameraAsync/launchImageLibraryAsync否则浏览器会静默拦截。这在 src/ImagePicker.ts 的 JSDoc 中有明确说明。四、SDK 10 ~ 12模块现代化与结果对象重构4.1 config plugin 的引入10.0.0从 10.0.0 起模块开始提供 config plugin使得在 managed 工作流中也能通过app.json配置原生权限文案。当前实现位于 plugin/src/withImagePicker.ts核心能力如下表配置项类型作用默认值photosPermissionstring \| false设置 iOSNSPhotoLibraryUsageDescriptionAllow $(PRODUCT_NAME) to access your photoscameraPermissionstring \| false设置 iOSNSCameraUsageDescriptionAllow $(PRODUCT_NAME) to access your cameramicrophonePermissionstring \| false设置 iOSNSMicrophoneUsageDescriptionfalse时同时屏蔽 AndroidRECORD_AUDIOAllow $(PRODUCT_NAME) to access your microphonecolors对象设置 Android 裁剪界面亮色主题颜色无dark.colors对象设置 Android 裁剪界面暗色主题颜色无colors对象支持五个键cropToolbarColor工具栏背景、cropToolbarIconColor工具栏图标、cropToolbarActionTextColor操作文字、cropBackButtonIconColor返回按钮、cropBackgroundColor裁剪背景在源码中分别映射到expoCropToolbarColor等 Android 颜色资源。4.2 iOS 结果对象重构14.0.014.0.0 将 iOS 模块从 ObjC 重写为 Swift并将结果对象简化为{ canceled, assets }结构。这一结构沿用至今定义在 src/ImagePicker.types.tsexport type ImagePickerResult ImagePickerSuccessResult | ImagePickerCanceledResult; // success: { canceled: false, assets: ImagePickerAsset[] } // canceled: { canceled: true, assets: null }同时MediaTypeOptions枚举被标记废弃16.0.0改为使用MediaType | MediaType[]type MediaType images | videos | livePhotos;废弃兼容逻辑在 src/utils.ts 的parseMediaTypes中实现它会打印弃用警告并做映射。五、SDK 13 ~ 14原生重写与系统 Photo Picker 集成5.1 Android 裁剪库迁移13.0.013.0.0 将裁剪库从com.theartofdev.edmodo:android-image-cropper迁移到com.github.CanHub:Android-Image-Cropper。当前版本已在此基础上进一步演进Android 端自实现了 ExpoCropImageActivity.kt 并在 Manifest 中将其exportedfalse同时显式覆盖库中的CropImageActivity为exportedfalse防止外部应用直接唤起见 55.0.20 的安全修复。5.2 Android 系统 Photo Picker14.3.014.3.0 让 Android 使用系统 Photo Picker界面更接近 iOS。当前 Android 实现通过 ImageLibraryContract.kt 调用ActivityResultContracts.PickMultipleVisualMedia/PickVisualMediadefaultTab选项photos | albumsAndroid 专属直接映射到系统选择器的初始页签见 ImagePickerOptions.kt 的DefaultTab枚举。5.3 iOS 多选与 Live Photo13.x ~ 14.x13.2.0 iOS 14 多选13.3.0 iOS 14 选择上限、iOS 15 有序选择orderedSelection14.0.0 默认使用PHPickerViewController16.0.0 支持从图库选择 Live Photo。当前 iOS 选择路径在 ios/ImagePickerModule.swift 的launchImagePicker中分流非编辑 非相机走PHPickerViewController多选否则走UIImagePickerControllerLegacy。Live Photo 通过mediaTypes: [livePhotos]开启此时返回的ImagePickerAsset包含pairedVideoAsset字段iOS 专属。六、SDK 15 ~ 17选项语义的定型与现代性能路径6.1 权限可禁用15.0.0与 quality 默认值变更16.0.015.0.0 允许在 config plugin 中传false禁用权限16.0.0 将quality默认值从0.2改为1.0更好性能且符合直觉。这两个语义在当前源码中均有体现withImagePicker.ts的withBlockedPermissions负责屏蔽权限ImagePickerOptions的默认quality 1.0。6.2 iOS 资源表示模式与 fast-path17.0.017.0.0 是近年最值得关注的一次重构preferredAssetRepresentationMode默认值从.automatic改为.current保持资源原始容器/编码如 HEIC 而非 JPEG这是新 fast-path 的前提想恢复旧行为的应用可显式传automatic。图片 fast-path当quality: 1allowsEditing: false 表示模式.current时原始文件只复制一次、从文件头读取尺寸不做完整解码/重编码。视频 PassthroughVideoExportPreset.Passthrough的视频不再转码、只复制一次。裁剪 UI 可定制Android引入 light/dark 主题支持与shape选项rectangle | oval。preferredAssetRepresentationMode的取值定义在 src/ImagePicker.types.ts 的UIImagePickerPreferredAssetRepresentationModeautomatic | compatible | currentiOS 端映射到PHPickerConfigurationAssetRepresentationMode。七、当前版本55/56/57核心 API 速查7.1 主要方法src/ImagePicker.ts方法说明launchImageLibraryAsync(options)打开系统相册选择器launchCameraAsync(options)打开系统相机iOS 模拟器自 56.0.16 起也可调用getCameraPermissionsAsync()/requestCameraPermissionsAsync()查询/请求相机权限getMediaLibraryPermissionsAsync(writeOnly)/requestMediaLibraryPermissionsAsync(writeOnly)查询/请求媒体库权限writeOnly默认falseuseCameraPermissions()/useMediaLibraryPermissions()权限 Hook 封装getPendingResultAsync()Android 专用恢复 Activity 被系统销毁后丢失的选择结果参数校验在validateOptions中进行aspect两个分量必须为正数、quality必须在[0, 1]、videoMaxDuration非负否则抛出ERR_INVALID_ARGUMENT。Android 端 ImagePickerOptions.kt 还通过FloatRange(0.0, 1.0)、IntRange(from 0)在原生层二次约束。7.2 常用选项一览import * as ImagePicker from expo-image-picker; const result await ImagePicker.launchImageLibraryAsync({ mediaTypes: [images, videos], // images | videos | livePhotos可传数组 allowsEditing: false, // 与 allowsMultipleSelection 互斥 aspect: [4, 3], // 仅 Android编辑时保持宽高比 shape: rectangle, // 仅 Androidrectangle | oval quality: 1.0, // 0 ~ 1默认 1.0 exif: false, // 是否返回 EXIFiOS 相机场景不含 GPS base64: false, // 是否附带 JPEG Base64 数据 allowsMultipleSelection: false, // 多选iOS 14 / Android / Web selectionLimit: 0, // 0 表示系统上限 orderedSelection: false, // iOS 15按选择顺序编号返回 defaultTab: photos, // Androidphotos | albums videoMaxDuration: 0, // 秒iOS 编辑模式上限 600 秒 presentationStyle: automatic, // iOSpresentation 样式 cameraType: back, // back | front preferredAssetRepresentationMode: current, // iOS 14默认 current legacy: false, // Android使用旧版选择器 shouldDownloadFromNetwork: false, // iOS允许从 iCloud 下载 });7.3 返回结果ImagePickerAssettype ImagePickerAsset { uri: string; // 本地文件 URIWeb 上为 blob URL见 17.0.0 assetId?: string | null; // 媒体库 ID可配合 expo-media-library 使用 width: number; // 可能为 0系统未提供 height: number; type?: image | video | livePhoto | pairedVideo | null; fileName?: string | null; fileSize?: number; exif?: Recordstring, any | null; // 需 exif: true base64?: string | null; // 需 base64: trueJPEG 数据 duration?: number | null; // 视频时长毫秒 mimeType?: string; pairedVideoAsset?: ImagePickerAsset | null; // iOS Live Photo file?: File; // Web 专属用于 FormData 上传 };八、Unpublished 待发布变更截至 57.0.7CHANGELOG 头部Unpublished记录了下一个版本的预期变更与源码相互印证新特性Android 端在返回的 EXIF 元数据中新增PhotographicSensitivity字段源码中 EXIF 提取逻辑位于 MediaHandler.kt。Bug 修复iOSlaunchCameraAsync后裁剪的边界计算错误#45554base64在allowsEditing: false时返回原始非 JPEG 数据#48005与 17.0.0 的 fast-path 相关裁剪失败时错误地返回未裁剪原图而非 reject#48524视频PHAssetResourceManagerfast-path 在 iCloud 资产上失败时未回退到慢速路径#48794以及未声明NSPhotoLibraryUsageDescription的应用在选择视频时被终止的问题#49362。Bug 修复Android为相机输出 URI 显式授予相机应用访问权以应对 Android 移除ACTION_IMAGE_CAPTURE隐式 URI 授权#46954修复videoMaxDuration选项#47504。其他Android 端ExpoCropImageActivity逐步脱离对CropImageActivity的依赖#47141。这些修复与 ios/MediaHandler.swift、ios/ImageUtils.swift 中的媒体处理管线直接相关先尝试 fast-path 直接复制原始文件失败后回退到完整解码路径。九、底层原理速览从 API 到原生管线9.1 iOS 选择路径ImagePickerModule.swiftlaunchCameraAsync / launchImageLibraryAsync └─ 权限检查相机场景必须已授予 Camera 权限 └─ launchImagePicker(sourceType) ├─ allowsEditing sourceType ! camera ? Legacy(UIImagePickerController) : MultiSelect(PHPickerViewController) └─ 结果回调 → MediaHandler.handleMedia / handleMultipleMedia → ImagePickerResponse关键细节相机 allowsEditing时会调用picker.fixCannotMoveEditingBox()修复编辑框无法移动的系统问题allowsEditing下videoMaxDuration超过 600 秒会直接 rejectMaxDurationWhileEditingExceededException0则自动设为 600iPad 上统一配置 popover 锚点避免 15.0.7 中的崩溃问题。9.2 Android 选择路径ImagePickerModule.ktlaunchCameraAsync └─ ensureTargetActivityIsAvailable系统是否有相机应用 └─ ensureCameraPermissionsAreGranted └─ 创建缓存临时文件 → Content URI → CameraContract(ACTION_IMAGE_CAPTURE) launchImageLibraryAsync └─ ImageLibraryContract(PickVisualMedia / PickMultipleVisualMedia) └─ 单选 allowsEditing 时级联 cropImageLauncherlaunchContract中通过isPickerOpen标志防止重复打开多个选择器15.0.3 的崩溃修复handleResultUponActivityDestruction把成功结果暂存到pendingMediaPickingResult供getPendingResultAsync()在 Activity 被销毁后恢复。9.3 相机输出 URI 授权Unpublished #46954Android 移除对ACTION_IMAGE_CAPTURE的隐式 URI 授权后模块需要在启动相机 Intent 时通过ClipData或grantUriPermission显式授权。这与 ImagePickerFileProvider.kt 及其路径配置image_picker_provider_paths.xml配合使用。十、从更新日志到工程实践的 5 条建议关注 fast-path 条件iOS 上若想享受零重编码性能需同时满足quality: 1、allowsEditing: false、preferredAssetRepresentationMode: current默认任何一项不满足都会走完整解码路径。明确编辑与多选的互斥allowsEditing与allowsMultipleSelection互斥后者开启时前者被忽略JS 层会console.warn提示。Web 端 blob URI从 17.0.0 起 Web 的uri是 blob URL 而非 base64 data URLbase64字段行为不变若需上传服务器推荐直接使用asset.fileFile对象配合FormData。Android 权限随版本变化Android 13 使用细粒度媒体权限READ_MEDIA_*16.0.0 已移除相关声明READ/WRITE_EXTERNAL_STORAGE仅在 API ≤ 32 生效。升级目标 SDK 时务必回归测试权限流程。iOS 模拟器可测相机从 56.0.16 起launchCameraAsync可在模拟器调用调用前仍建议用useCameraPermissions处理授权状态。十一、相关源码索引类型与选项定义src/ImagePicker.types.tsJS API 入口与参数校验src/ImagePicker.tsWeb 实现src/ExponentImagePicker.web.ts配置插件plugin/src/withImagePicker.tsAndroid 模块与契约ImagePickerModule.kt、ImageLibraryContract.ktiOS 模块与媒体处理ImagePickerModule.swift、MediaHandler.swift测试用例ImagePicker-test.native.ts、MediaHandlerTests.swift、DimensionsExporterTests.kt模块清单与元数据expo-module.config.json、package.json通过以上演进脉络可以看出expo-image-picker的价值不仅在于调用系统选择器这一层薄封装更在于它对三个平台数十个系统版本差异的收敛从权限模型Android 13 细粒度、iOS 14 受限库、资源表示HEIC/AVIF/TIFF 原样保留、到性能路径fast-path 复制 vs 完整重编码。理解 CHANGELOG 中每一条记录背后的源码实现就能在升级 SDK 时准确预判行为变化写出跨平台稳定的图片选择代码。【免费下载链接】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),仅供参考