跳过主要内容
移动 指南

2026年Expo图像选择器指南

在您的React Native应用程序中掌握Expo图像选择器。 本完整指南涵盖了安装、权限、摄像头/相册访问、裁剪、base64和上传。

2026年Expo图像选择器指南

您可能已经到了UI准备好的阶段,配置文件屏幕上有一个“上传照片”按钮,但现在突然很难了。实际的图像选择流程涉及原生权限、操作系统控制的界面、与许多开发人员预期不同的返回形状以及在部署真实构建后才会出现的构建时间细节。

这就是Expo图像选择器的作用。它是Expo官方图像选择器库,用于打开系统UI选择设备库中的图像和视频或使用摄像头拍摄照片,正如在 Expo包仓库中描述的那样。在实践中,这意味着您获得了可靠的原生媒体输入桥梁,但不是在每个设备上都表现一致的自定义媒体体验。

本指南是针对首次实现而写的,而不是演示。它关注的是在生产中重要的决策:管理工作流设置、不会让您在后来感到惊讶的权限处理、安全结果解析以及在用户选择文件后实用的上传模式。如果您正在使用自定义原生设置,了解此如何与 Expo开发客户端工作流.

的区别也很有帮助。

使用 Expo 图像选择器

产品经理要求用户上传头像。一个星期后,同样的功能还需要用户上传收据、拍摄事故报告照片,并且在用户第一次拒绝权限时进行重试。图像输入功能快速扩展,因为它涉及到原生权限、操作系统拥有的 UI、临时文件处理和后端上传流程。

expo-image-picker 是 Expo 的 SDK 模块,用于完成此项任务。它打开平台选择器或相机 UI,并返回选定的媒体以便 React Native code 可以处理。JavaScript API 的代码量很小。主要挑战在于在管理和裸机项目中正确设置原生配置、权限流程和结果处理。

主要的权衡是明确的。您让 iOS 和 Android 来呈现自己的媒体 UI,而不是在 JavaScript 中构建一个自定义选择器。这通常会给出更好的结果:用户已经理解系统屏幕,权限提示按照操作系统的期望行为,且您的团队避免了维护一个图库实现的 JavaScript 代码。

将其视为一个原生集成功能,具有 React 接口。

这种思维方式有帮助,因为故障模式通常不在调用选择器的按钮中。它们通常来自以下三个地方:

  • 原生配置: 缺少插件设置、错误的权限字符串或是更改配置后过时的构建
  • 运行时行为: 用户可以拒绝访问、在 iOS 上授予有限的库访问权限,或者在没有选择任何内容的情况下取消流程
  • 结果解析: 当前API返回一个 assets 数组,所以旧的示例直接读取 result.uri 会失败

工作流程选择也会改变设置路径。在一个受管理的 Expo 应用中,大部分本机工作都在 app 配置中,需要在配置发生变化时重建。在一个裸应用中,您仍然可以获得 Expo 模块API,但您需要直接验证 iOS 和 Android 项目设置。如果您的团队正在使用自定义客户端而不是 Expo Go,则本指南与Capgo的解释相配,后者解释了 如何使用 Expo 开发客户端更改本机模块测试.

这两种工作流程的区别对于本指南的其余部分至关重要,因为happy path 只是故事的一半。一个选择器实现是坚固的,当它在两种工作流程中都有效,处理平台特定的权限奇怪之处而不惊吓用户,并将可用的文件传递给上传层时,停止在本地预览上。

安装和基本配置

安装只需要一个命令。正确的本机配置是决定选择器在真实设备、自定义开发客户端和生产构建中是否有效的关键因素。

expo-image-picker 为 React 提供了一个面向API的平台选择器,用于照片、视频和摄像头捕获。JavaScript 调用简单。设置不是,因为照片访问和摄像头访问由 iOS 和 Android 控制,而不是 React Native。

一个开发者正在使用code在笔记本电脑屏幕上实现Expo图像选择器。

使用 Expo 的版本感知安装器:

npx expo install expo-image-picker

使用 expo install 而不是 npm install 或 yarn add. Expo matches the package version to your SDK, which avoids a common class of native compatibility problems. If you are comparing how Expo modules fit into your release process, this 在管理工作流中,声明插件在应用配置中,以便 Expo 在构建时应用原生更改。 例如使用

这是最小的设置。实际上,团队通常还添加权限文本,尤其是在 iOS 上,系统提示应该解释为什么应用需要访问。保持措辞具体到用户操作。 "上传个人资料照片"比 "需要媒体访问"更好。

Expo 工具概述

是有用的参考。 app.json:

{
  "expo": {
    "plugins": ["expo-image-picker"]
  }
}

在管理工作流中设置

一个操作细节会浪费很多时间。改变 plugins,权限字符串或其他本机配置需要重新构建。重新加载 JavaScript 不会应用这些更改。在 Expo Go 中,您还受到客户端已经包含的限制。在开发构建或生产构建中,native 项目仅在重新构建后才反映您的配置。

React Native bare 设计细节

在一个 bare 应用中,包 API 是相同的,但您需要自己验证更多的 native 项目。iOS 使用说明是首先要检查的。如果您的流程可以打开库,启动相机或录制视频,音频,您的应用需要相应的权限字符串在 Info.plist 重新构建之前。

bare 项目的实用检查清单如下:

  1. 安装 expo-image-picker 使用 npx expo install expo-image-picker.
  2. 如果您的项目使用 Expo 配置插件,则添加插件配置。
  3. 确认 iOS 使用说明与您暴露的功能匹配。
  4. 重新构建 iOS 和 Android 应用程序后,任何本机配置更改。

缺少权限文本通常看起来像一个运行时错误,因为 UI code 是正常的,按钮处理程序运行。失败是更低的堆栈。通常我会检查 Info.plist, the app config, and whether the current build includes the latest native changes before I touch the component code.

几个习惯使设置变得更加可预测:

  • 写入实际操作的权限文本: 用户应该了解为什么他们看到提示。
  • 分别配置相机和库: 一个可以工作,而另一个仍然失败。
  • 重建后本机更改: 热重载和快速刷新不会更新本机权限。
  • 在设备上测试: 模拟器行为可能会隐藏权限和相机问题。

如果在开发期间选择器正常工作,但在 TestFlight 或 Play Store 构建中出现问题,请先将其视为配置问题。通常情况下,这是正确的。

访问相机和媒体库

A用户点击“上传照片”,期望相机或图库打开,而您的应用在那一刻有一个任务。打开正确的系统UI,处理拒绝或取消而不破坏屏幕,并返回可用的本地文件引用用于预览或上传。

直到您测试iOS和Android的管理和裸机构建时,听起来很简单。JavaScriptAPI保持紧凑,但运行时行为仍然依赖于OS提示、设备硬件以及您之前配置的本机权限。

流程图,展示了选择相机或媒体库源的移动应用图像选择器流程。

一个最小化但安全的组件

核心流程在Expo管理和裸机工作流项目中保持一致。请求相关权限,启动选择器,检查用户是否取消,然后读取第一个资产从 result.assets.

一个基线组件看起来像这样:

import { useState } from 'react';
import { View, Button, Image, Alert } from 'react-native';
import * as ImagePicker from 'expo-image-picker';

export default function PhotoInput() {
  const [imageUri, setImageUri] = useState<string | null>(null);

  const pickFromLibrary = async () => {
    const permission = await ImagePicker.requestMediaLibraryPermissionsAsync();

    if (!permission.granted) {
      Alert.alert('Permission required', 'Please allow photo library access.');
      return;
    }

    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ['images'],
      allowsEditing: true,
      quality: 1,
    });

    if (result.canceled) return;

    const asset = result.assets?.[0];
    if (!asset?.uri) return;

    setImageUri(asset.uri);
  };

  const takePhoto = async () => {
    const permission = await ImagePicker.requestCameraPermissionsAsync();

    if (!permission.granted) {
      Alert.alert('Permission required', 'Please allow camera access.');
      return;
    }

    const result = await ImagePicker.launchCameraAsync({
      allowsEditing: true,
      quality: 1,
    });

    if (result.canceled) return;

    const asset = result.assets?.[0];
    if (!asset?.uri) return;

    setImageUri(asset.uri);
  };

  return (
    <View>
      <Button title="Choose from library" onPress={pickFromLibrary} />
      <Button title="Take photo" onPress={takePhoto} />
      {imageUri ? (
        <Image
          source={{ uri: imageUri }}
          style={{ width: 200, height: 200 }}
        />
      ) : null}
    </View>
  );
}

这里有三个细节。

  • 分别请求图库和相机权限。它们独立失败。
  • 将取消视为正常用户行为,而不是错误状态。
  • 读取 assets[0]图库和相机流程 uri.

图库和相机流程

如果您想快速实现功能,建议使用库流程。它更容易测试,兼容更多的模拟器设置,并避免了相机硬件边缘案例。等结果处理路径稳定后再添加相机支持。

相机路径在开发中有更多的可能会失败。iOS模拟器的支持有限。Android模拟器可能不会暴露相机行为与真实设备匹配。在裸项目中,这些缺口可能会让您查看组件code,尽管实际问题是本机配置或测试环境。

清晰的UI模式是在调用选择器API之前,要求用户提供源文件。

const showPickerOptions = () => {
  Alert.alert('Upload image', 'Choose a source', [
    { text: 'Camera', onPress: takePhoto },
    { text: 'Photo Library', onPress: pickFromLibrary },
    { text: 'Cancel', style: 'cancel' },
  ]);
};

这种分离使每个功能保持专注。它还使得添加分析、特性标志或后端特定规则变得更加容易。例如,一些团队允许库上传用于个人资料图片,但要求最新的相机捕获用于身份验证。

如果您的应用程序也支持Expo外的文件访问模式,或者您正在比较原生堆栈的约定,这个 Capacitor照片库参考 对您有用。

一个短的演示会有所帮助,当您向同事或QA展示此流程时。

您期望的系统UI

expo-image-picker 打开平台选择器或相机UI。您的应用程序并不控制该流程中的每个屏幕。这种区别很重要,因为“在我的设备上工作”通常意味着“OS允许我测试的路径。”

On iOS,用户可能授予有限的库访问权限,而不是完全访问权限。 在 Android 上,选择器行为可能因 OS 版本和供应商皮肤而异。 在管理工作流项目中,Expo 为您处理更多的本机编程。 在裸工作流项目中,您需要确认您的构建应用程序包含您所做的本机权限更改。 JavaScript 调用站点在两种情况下可能相同,而运行时结果则不同。

I 的测试通常在调用功能完成之前进行:

  • 首次权限请求
  • 拒绝权限
  • 用户取消
  • 成功的库选择
  • 成功的物理设备摄像头捕获
  • 立即预览返回的本地 URI

这些情况直接映射到真实的生产行为。它们还清晰地设置了下一步,如果您需要将文件发送到服务器、审核管道或发布端点(例如 Instagram 媒体发布 API.

处理选择器结果和选项

选择器结果是通常需要真实生产逻辑的部分。系统 UI 返回一个结构化的对象,而不是仅仅是一个文件路径,且这里的小错误会导致预览破裂、空白上传或用户取消后崩溃。

正确读取结果对象

当前Expo应用中关注的结果形状是 result.assets[0].uri,而不是顶级 result.uri. 这个细节会影响两种工作流程项目:管理和裸露,因为JavaScript API 在两种情况下都是相同的,尽管原生设置在底层是不同的

使用守卫式模式:

const result = await ImagePicker.launchImageLibraryAsync({
  mediaTypes: ['images'],
  allowsEditing: true,
  quality: 1,
});

if (result.canceled) {
  return;
}

const asset = result.assets?.[0];
if (!asset) {
  return;
}

const { uri } = asset;
setImageUri(uri);

这处理我经常遇到的两个失败案例。取消的选择器不会给你一个可以读取的资产,而 code 会假设 result.assets[0] 总是存在会在运行时失败

一旦你有了URI,渲染预览就很简单:

<Image source={{ uri: imageUri }} style={{ width: 240, height: 240 }} />

如果你打算以后上传,保留整个 asset 对象,而不是仅仅保留URI。在实际中 fileName, mimeType, width, height和 fileSize 经常有用来验证、记录日志或构建一个更干净的多部分请求

影响下游行为的选项

Several picker options affect more than the selection screen. They shape file size, editing behavior, and what your backend has to accept.

Option Type context: Page/area: About Capgo page. Role: Short UI label or navigation item. Seen in: page about.astro. Message key `about_facts_type` (About Facts Type). What it changes
mediaTypes Typical use array Limit selection to images if your API only accepts images
allowsEditing boolean boolean Let the OS offer crop or edit UI where supported
quality 数字 支持的图像输出压缩 减少移动网络上传大小
base64 布尔值 将编码的图像数据添加到结果中 仅用于显式要求内联图像数据的集成

容易忽略的几个权衡:

  • allowsEditing 在图像插槽具有固定的形状或大小时很有用。 如果您的服务器处理裁剪管道并希望原始文件,则其用处较小。
  • quality 会影响上传时间、内存压力和服务器存储。 quality: 1 不是自动正确的选择。
  • mediaTypes 应与后端规则匹配。如果服务器拒绝视频,请不要让选择器返回它们。
  • base64 会增加内存中的负载大小。除非接收服务要求其它,否则请避免使用它。

对于低内存设备来说,这一点很重要。

通常预览和多部分上传时,使用本地文件URI是更好的转交方式。

Base64有合适的使用场景,但与传递文件引用相比,它是昂贵的。

  • URI与Base64 URI 使用
  • URI与Base64 URI URI
  • URI与Base64 预览时 使用

保持选择器code小巧并易于测试的模式。它还与后端媒体流的构建方式一致,包括最终发布到外部平台的服务,如 Instagram媒体发布API.

如果您的团队频繁发布OTA更新或移动图像密集的资产,则在此处的文件大小决策将影响整个管道。这篇关于 优化应用更新的图像 的指南是选择器配置的有用补充。

用于真实应用的安全结果模式

对于演示code,仅存储 imageUri 是足够的。在生产环境中,存储一个标准化的对象,以便下一步(预览、验证、上传或重试)不需要重新解释原始选择器响应。

const result = await ImagePicker.launchImageLibraryAsync({
  mediaTypes: ['images'],
  allowsEditing: true,
  quality: 0.8,
});

if (result.canceled || !result.assets?.length) {
  return;
}

const asset = result.assets[0];

setSelectedImage({
  uri: asset.uri,
  fileName: asset.fileName ?? 'upload.jpg',
  mimeType: asset.mimeType ?? 'image/jpeg',
  width: asset.width,
  height: asset.height,
  fileSize: asset.fileSize ?? null,
});

这给您一个可预测的形状,使应用code在您处理本机差异时保持稳定。

最后一步检查有助于。不要仅仅为了万一而启用额外的结果字段。请求您知道需要的数据,并保持选择器专注于选择,而不是将其转换为一般文件处理步骤。

高级模式和平台差异

选择器功能通常在第一个选定的图像必须存活于重试、认证头、native权限差异和真实上传端点时就不再简单了。 expo-image-picker 处理选择很好。其他功能都在你的应用程序中。

一张名为《本地存储与服务器存储的考虑》的图表,展示了服务器存储的优缺点。

实用的上传模式

对于期望文件上传的API来说, FormData 仍然是最安全的默认设置。它可以在常见的Rails、Node、Laravel、Django和Go后端上工作,并且将选择器与传输问题分开。

async function uploadImage(imageUri: string) {
  const formData = new FormData();

  formData.append('file', {
    uri: imageUri,
    name: 'upload.jpg',
    type: 'image/jpeg',
  } as any);

  const response = await fetch('https://your-api.example.com/uploads', {
    method: 'POST',
    body: formData,
    headers: {
      Accept: 'application/json',
    },
  });

  if (!response.ok) {
    throw new Error('Upload failed');
  }

  return response.json();
}

即使只有code,也足以证明路径有效,但生产应用程序通常需要一个额外的层次。从 name 和 type 当可能时,从选择的资产中派生,外部将认证附加到选择器函数中,并将上传状态与选择器状态分开,以便失败的请求不会迫使用户重新打开库。

几个检查可以防止我在审查中看到的常见故障:

  • 确认本地 uri 存在之前构建请求
  • 在上传之前渲染预览,以便用户可以在错误文件上早早发现
  • 防止在请求飞行期间重复点击
  • 单独处理网络故障、选择器取消或权限错误
  • 预期后端验证拒绝大文件、不支持的MIME类型或缺失的认证

如果您的后端要求base64而不是多部分,那通常是一个服务器约束,而不是选择器要求。多部分在内存上更便宜,且在移动设备上更容易理解。

在哪里平台差异实际上很重要

选择器UI是本地的,因此继承了本地行为。 这影响了用户看到的内容以及您的code应该假设的内容。

在iOS上,编辑流程和权限提示遵循苹果的惯例。 有限的照片访问权限可能会返回比您在完全授权设备上看到的测试帐户看到的资产更窄的集合。 在Android上,选择器行为在OS版本和制造商皮肤方面会有更多的差异,尤其是在专辑、文件名和如何返回相机捕获方面。 仅有的React Native应用程序更直接地感受到这些差异,因为您拥有更多的本机设置,但管理的Expo应用程序仍然需要code,将选择器视为平台形状而不是完美统一的。

实践规则很简单。 依赖于可以验证的字段,而不是跨设备的UI或元数据相同。

几个例子在实际应用中很重要:

  • 编辑和裁剪: UI和裁剪行为在iOS和Android之间不相同
  • 返回的元数据: fileName, mimeType,和 fileSize 可以缺失或不一致,所以添加fallback
  • 权限: iOS相机访问可以限制到选定的项目,而Android行为取决于OS版本和系统选择器支持
  • 相机输出: 捕获的图片可能带回不同的命名、方向或压缩特征与库资产

如果您的团队也在Expo外工作,这 设计栈应用开发指南 给出了有用的Android媒体处理决策的上下文,显示在单个库之外

管理和裸露工作流程的区别

在这个阶段,设置选择开始影响操作

在管理工作流中,权限字符串和插件配置通常存储在应用配置中,而原生更改在创建新构建时应用。这样可以保持JavaScript表面清洁,但这也意味着配置修复在下一次原生构建之前不可见。OTA更新不修复缺失的原生权限

In bare workflow 中,相同的功能有更多的移动部分。您需要自己验证原生 iOS 使用说明、Android 清单行为、包安装和重建时间。控制的好处是,您可以控制每个部分。但是,这也意味着,如果出现 picker 问题,它可能是由原生配置而不是 JavaScript 调用站点引起的。

从 Expo Switch 到 Capacitor 的团队经常低估了抽象层之间的差异。Capgo 有一个有用的解释,说明 Capacitor 如何处理平台差异。 如何Capacitor处理平台差异我在两种工作流中都有相同的偏好。保持 picker __CAPGO_KEEP_0__ 灵活,normalize 结果一次,通过专门的 __CAPGO_KEEP_1__层上传,并将平台特定行为视为需要配置和测试的内容,而不是通过假设来平滑处理。

My preference is consistent across both workflows. Keep picker code narrow, normalize the result once, upload through a dedicated API layer, and treat platform-specific behavior as something to configure and test explicitly rather than smooth over with assumptions.

大多数 Expo Image Picker 错误都属于小型分类。最快的修复方法通常是确定哪个层次失败:配置、权限、结果处理或渲染。

使用 expo-image-picker 库在移动开发项目中排查常见问题的检查表。

快速检查常见故障

如果 picker 不会打开或权限失败,请先检查原生设置。在无裸应用中,缺少的 iOS 使用说明是常见的根源原因。

Fast checks for common failures

如果应用程序在用户关闭选择器后崩溃,请检查您的结果处理。许多实现仍然假设直接 URI 并跳过检查。 canceled 检查。

快速映射帮助:

  • 权限被拒绝的错误: 验证您的应用程序配置和本机权限字符串,然后重建。
  • undefined 图像 URI: 从 result.assets?.[0]?.uri,而不是 result.uri.
  • 取消后什么都没有发生: 这可能是正确的。将取消视为无操作状态。
  • 图像未渲染: 确认 URI 已存储在状态中并传递到 <Image source={{ uri }} />.
  • 模拟器中摄像头行为异常: 在追踪库bug之前,请在物理设备上进行测试。

生产检查清单

在发布前进行最后一次检查:

  • 使用Expo工具安装: 使用 npx expo install expo-image-picker.
  • 配置原生组件: 添加插件和所需权限的描述。
  • 故意请求权限: 分离摄像头和媒体库流程。
  • 保护每个结果: 检查 result.canceled 并且安全地读取 assets[0].
  • 优先使用 URI 协议上传: 仅在特殊情况下保留 base64。
  • 测试真实设备: 尤其适用于摄像头捕获和权限提示。

如果您的团队同时部署 Capacitor 或 Electron 应用程序和 React Native 项目 Capgo 是将 JavaScript、CSS、配置和资产更新推送到应用程序而无需等待每次更改都经过商店审查的选项。它适用于图像相关修复位于您的 Web 层中,例如上传 UI、验证规则、复制或处理图像选择流程中的资产。

Capacitor 应用的即时更新

When a web-layer bug is live, ship the fix through Capgo instead of waiting days for app store approval. Users get the update in the background while native changes stay in the normal review path.

来自 Martin 的人性化支持

立即开始

最新博客

Capgo 为您提供创建真正专业的移动应用所需的最佳见解