欢迎加入开源鸿蒙跨平台社区 :https://openharmonycrossplatform.csdn.net
📋 前言
在移动应用开发中,图片选择和拍照是最常见的功能之一。无论是用户头像上传、图片分享、还是证件照采集,都需要可靠的图片选择组件。@react-native-ohos/react-native-image-picker 是 React Native 社区广泛使用的图片选择库,支持从相册选择图片/视频,提供了丰富的配置选项和回调数据,是 RN 项目中图片处理的首选方案。
🎯 库简介
基本信息
- 库名称 :
@react-native-ohos/react-native-image-picker - 版本信息 :
7.0.4: 支持 RN 0.72 版本8.2.2: 支持 RN 0.77 版本
- 官方仓库: https://github.com/react-native-oh-library/react-native-image-picker
- 主要功能 :
- 🖼️ 从相册选择图片或视频
- 📊 支持多选和单选模式
- 🔧 丰富的配置选项
- 📦 返回详细的媒体信息
为什么需要 Image Picker?
| 特性 | 手动实现 | Image Picker |
|---|---|---|
| 相册访问 | ❌ 需原生代码 | ✅ 统一API |
| 多选支持 | ⚠️ 复杂实现 | ✅ 配置即可 |
| 媒体信息 | ❌ 需额外处理 | ✅ 自动返回 |
| Base64转换 | ❌ 需手动实现 | ✅ 一键开启 |
| HarmonyOS支持 | ❌ 不支持 | ✅ 完整支持 |
⚠️ 已知限制
| 功能 | 状态 | 说明 |
|---|---|---|
| 相机拍照 | ❌ 暂不支持 | HarmonyOS 相机功能暂未适配 |
| 相机录制视频 | ❌ 暂不支持 | HarmonyOS 相机功能暂未适配 |
| 相册选择 | ✅ 支持 | 完整支持图片和视频选择 |
兼容性验证
在以下环境验证通过:
- RNOH : 0.72.90; SDK : HarmonyOS 6.0.0 Release SDK; IDE : DevEco Studio 6.0.0.858; ROM: 6.0.0.112
📦 安装步骤
1. 使用 npm 安装
在项目根目录执行以下命令,本文基于 RN 0.72.90 版本开发:
bash
# RN 0.72 版本推荐使用
npm install @react-native-ohos/react-native-image-picker@7.0.4-rc.1
# 或者使用 yarn
yarn add @react-native-ohos/react-native-image-picker@7.0.4-rc.1
2. 验证安装
安装完成后,检查 package.json 文件,应该能看到新增的依赖:
json
{
"dependencies": {
"@react-native-ohos/react-native-image-picker": "7.0.4-rc.1",
// ... 其他依赖
}
}
🔧 HarmonyOS 平台配置 ⭐
由于 HarmonyOS 暂不支持 AutoLink,需要手动配置原生端代码。本文提供 HAR 包引入 和 源码引入 两种方式,可根据实际需求选择。
1. 在工程根目录的 oh-package.json5 添加 overrides 字段
打开 harmony/oh-package.json5,添加以下配置:

json5
{
// ... 其他配置
"overrides": {
"@rnoh/react-native-openharmony": "0.72.90"
}
}
方式一:HAR 包引入(推荐)📦

HAR 包引入方式简单快捷,适合大多数场景。我使用这种方式进行引入,比较方便
💡 提示 :HAR 包位于三方库安装路径的
harmony文件夹下。
2.1 在 entry/oh-package.json5 添加依赖
打开 harmony/entry/oh-package.json5,添加以下依赖:
json5
"dependencies": {
"@rnoh/react-native-openharmony": "0.72.90",
+ "@react-native-ohos/react-native-image-picker": "file:../../node_modules/@react-native-ohos/react-native-image-picker/harmony/image_picker.har"
}
2.2 同步依赖
点击 DevEco Studio 右上角的 sync 按钮,或者在终端执行:
bash
cd harmony/entry
ohpm install
2.3 配置 CMakeLists.txt
打开 harmony/entry/src/main/cpp/CMakeLists.txt,添加以下配置(只需要添加带+号的):
cmake
project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")
set(LOG_VERBOSITY_LEVEL 1)
set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
set(WITH_HITRACE_SYSTRACE 1)
add_compile_definitions(WITH_HITRACE_SYSTRACE)
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
# 添加 ImagePicker 模块(HAR方式)
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-image-picker/src/main/cpp" ./image_picker)
file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")
add_library(rnoh_app SHARED
${GENERATED_CPP_FILES}
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)
# 链接 ImagePicker 库
+ target_link_libraries(rnoh_app PUBLIC rnoh_image_picker)
2.4 修改 PackageProvider.cpp
打开 harmony/entry/src/main/cpp/PackageProvider.cpp,添加:
cpp
#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
+ #include "RNImagePickerPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<RNOHGeneratedPackage>(ctx),
+ std::make_shared<RNImagePickerPackage>(ctx),
};
}
2.5 在 ArkTs 侧引入 ImagePickerViewPackage
打开 harmony/entry/src/main/ets/RNPackagesFactory.ts,添加:
typescript
import type { RNPackageContext, RNPackage } from 'rnoh/ts';
+ import { ImagePickerViewPackage } from '@react-native-ohos/react-native-image-picker/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
// ... 其他包
+ new ImagePickerViewPackage(ctx),
];
}
方式二:源码引入 📁
源码引入方式适合需要调试或修改原生代码的场景。
2.1 复制源码到 harmony 工程根目录
把 <RN工程>/node_modules/@react-native-ohos/react-native-image-picker/harmony 目录下的源码 image_picker 复制到 harmony(鸿蒙壳工程)工程根目录下。

2.2 在 build-profile.json5 添加模块
打开 harmony/build-profile.json5,添加以下模块:
json5
modules: [
// ... 其他模块
+ {
+ name: 'image_picker',
+ srcPath: './image_picker',
+ }
]
💡 提示 :如果存在
build-profile.template.json5文件,也需要同步添加上述模块配置。
2.3 修改 image_picker/oh-package.json5
打开 harmony/image_picker/oh-package.json5,修改 react-native-openharmony 版本与项目版本一致:
json5
{
"dependencies": {
"@rnoh/react-native-openharmony": "0.72.90"
}
}
2.4 在 entry/oh-package.json5 添加依赖
打开 harmony/entry/oh-package.json5,添加以下依赖:
json5
"dependencies": {
"@rnoh/react-native-openharmony": "0.72.90",
+ "@react-native-ohos/react-native-image-picker": "file:../image_picker"
}
2.5 修改ts文件后缀
如果 harmony/image_picker/ts文件的后缀是ts,调整为ets
2.6 同步依赖
点击 DevEco Studio 右上角的 sync 按钮,或者在终端执行:
bash
cd harmony/entry
ohpm install
2.6 配置 CMakeLists.txt
打开 harmony/entry/src/main/cpp/CMakeLists.txt,添加以下配置:
cmake
project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")
set(LOG_VERBOSITY_LEVEL 1)
set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
set(WITH_HITRACE_SYSTRACE 1)
add_compile_definitions(WITH_HITRACE_SYSTRACE)
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
# 添加 ImagePicker 模块(源码方式)
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-image-picker/src/main/cpp" ./image_picker)
file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")
add_library(rnoh_app SHARED
${GENERATED_CPP_FILES}
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)
# 链接 ImagePicker 库
+ target_link_libraries(rnoh_app PUBLIC rnoh_image_picker)
2.7 修改 PackageProvider.cpp
打开 harmony/entry/src/main/cpp/PackageProvider.cpp,添加:
cpp
#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
+ #include "RNImagePickerPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<RNOHGeneratedPackage>(ctx),
+ std::make_shared<RNImagePickerPackage>(ctx),
};
}
2.8 在 ArkTs 侧引入 ImagePickerViewPackage
打开 harmony/entry/src/main/ets/RNPackagesFactory.ts,添加:
typescript
import type { RNPackageContext, RNPackage } from 'rnoh/ts';
+ import { ImagePickerViewPackage } from '@react-native-ohos/react-native-image-picker/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
// ... 其他包
+ new ImagePickerViewPackage(ctx),
];
}
3. 同步并运行
点击 DevEco Studio 右上角的 sync 按钮,然后编译运行即可。
📖 API 详解
🔷 静态方法
Image Picker 提供两个核心静态方法:
| 方法 | 说明 | HarmonyOS 支持 |
|---|---|---|
launchCamera() |
调用相机拍照或录制视频 | ❌ 暂不支持 |
launchImageLibrary() |
从相册选择图片或视频 | ✅ |
typescript
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
// ⚠️ 调用相机 - HarmonyOS 暂不支持
// launchCamera(options, callback);
// ✅ 打开相册 - HarmonyOS 支持
launchImageLibrary(options, callback);
🔷 Options 配置属性
Options 对象用于配置图片选择器的行为,以下是各属性的详细应用说明。
1. mediaType - 媒体类型 ⭐
mediaType 属性用于指定选择器支持的媒体类型,是最常用的配置项。
typescript
mediaType: 'photo' | 'video' | 'mixed';
| 值 | 说明 | HarmonyOS 支持 |
|---|---|---|
photo |
仅选择图片 | ✅ |
video |
仅选择视频 | ✅ |
mixed |
图片和视频均可 | ✅ |
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景1:选择头像(仅图片)
const selectAvatar = () => {
launchImageLibrary(
{ mediaType: 'photo', selectionLimit: 1 },
(response) => {
if (response.assets?.[0]) {
setAvatar(response.assets[0].uri);
}
}
);
};
// 场景2:选择视频
const selectVideo = () => {
launchImageLibrary(
{ mediaType: 'video', selectionLimit: 1 },
(response) => {
if (response.assets?.[0]) {
setVideoUri(response.assets[0].uri);
}
}
);
};
// 场景3:图片或视频均可
const selectMedia = () => {
launchImageLibrary(
{ mediaType: 'mixed', selectionLimit: 9 },
(response) => {
if (response.assets) {
setMediaList(response.assets);
}
}
);
};
2. selectionLimit - 选择数量限制 📊
selectionLimit 属性用于控制可选择的媒体数量。
typescript
selectionLimit: number; // 默认值为 1
| 值 | 说明 | HarmonyOS 支持 |
|---|---|---|
1 |
单选模式(默认) | ✅ |
n |
最多选择 n 张 | ✅ |
0 |
不限制数量(HarmonyOS 最多 50 张) | ✅ |
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景1:单选头像
const selectSingleImage = () => {
launchImageLibrary(
{ mediaType: 'photo', selectionLimit: 1 },
handleResponse
);
};
// 场景2:最多选择 3 张图片
const selectUpToThree = () => {
launchImageLibrary(
{ mediaType: 'photo', selectionLimit: 3 },
handleResponse
);
};
// 场景3:多选模式(不限制)
const selectMultiple = () => {
launchImageLibrary(
{ mediaType: 'photo', selectionLimit: 0 },
handleResponse
);
};
3. cameraType - 相机类型 📷 ⚠️
⚠️ HarmonyOS 暂不支持: 由于相机功能未适配,此属性在 HarmonyOS 上无效
cameraType 属性用于指定使用前置或后置摄像头。
typescript
cameraType: 'back' | 'front';
| 值 | 说明 | HarmonyOS 支持 |
|---|---|---|
back |
后置摄像头(默认) | ❌ 暂不支持 |
front |
前置摄像头 | ❌ 暂不支持 |
💡 提示 :此属性仅在 iOS/Android 平台的
launchCamera方法中有效,HarmonyOS 暂不支持相机功能。
4. includeBase64 - Base64 编码 🔤
includeBase64 属性用于是否返回图片的 Base64 编码字符串。
typescript
includeBase64: boolean; // 默认为 false
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景1:获取 Base64 用于上传
const selectWithBase64 = () => {
launchImageLibrary(
{ mediaType: 'photo', selectionLimit: 1, includeBase64: true },
(response) => {
if (response.assets?.[0]) {
const asset = response.assets[0];
// 直接使用 base64 上传
uploadImage(asset.base64);
}
}
);
};
// 场景2:显示预览图
const showPreview = () => {
launchImageLibrary(
{ mediaType: 'photo', includeBase64: true },
(response) => {
if (response.assets?.[0]?.base64) {
setImageSource(`data:image/jpeg;base64,${response.assets[0].base64}`);
}
}
);
};
⚠️ 注意:大图片使用 Base64 会影响性能,建议仅在必要时开启。
🔷 Response 回调对象
Response 对象包含选择器的返回结果,以下是各属性的详细应用说明。
1. didCancel - 用户取消 🚫
didCancel 属性表示用户是否取消了选择操作。
typescript
didCancel: boolean;
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
const selectImage = () => {
launchImageLibrary({ mediaType: 'photo' }, (response) => {
if (response.didCancel) {
console.log('用户取消了选择');
return;
}
// 处理选中的图片
});
};
2. assets - 媒体资源数组 🖼️
assets 属性是包含所有选中媒体的数组,每个元素是一个 Asset 对象。
typescript
assets: Asset[];
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
const selectImages = () => {
launchImageLibrary(
{ mediaType: 'photo', selectionLimit: 0 },
(response) => {
if (response.assets && response.assets.length > 0) {
// 获取所有选中图片的 URI
const uris = response.assets.map(asset => asset.uri);
setImageUris(uris);
// 显示选中数量
console.log(`已选择 ${response.assets.length} 张图片`);
}
}
);
};
🔷 Asset 资源对象
Asset 对象包含单个媒体资源的详细信息,以下是各属性的详细应用说明。
1. uri - 文件路径 📁
uri 属性是选中媒体的文件路径,是最常用的属性。
typescript
uri: string;
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
import { Image } from 'react-native';
// 场景1:显示选中的图片
const ImagePickerDemo = () => {
const [imageUri, setImageUri] = useState<string | null>(null);
const selectImage = () => {
launchImageLibrary({ mediaType: 'photo' }, (response) => {
if (response.assets?.[0]?.uri) {
setImageUri(response.assets[0].uri);
}
});
};
return (
<View>
{imageUri && (
<Image source={{ uri: imageUri }} style={{ width: 200, height: 200 }} />
)}
<Button title="选择图片" onPress={selectImage} />
</View>
);
};
// 场景2:上传图片
const uploadSelectedImage = async () => {
const response = await launchImageLibrary({ mediaType: 'photo' });
if (response.assets?.[0]?.uri) {
const formData = new FormData();
formData.append('image', {
uri: response.assets[0].uri,
type: response.assets[0].type,
name: response.assets[0].fileName,
});
await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData,
});
}
};
2. width 和 height - 图片尺寸 📐
width 和 height 属性表示图片的宽度和高度(像素)。
typescript
width: number;
height: number;
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景1:检查图片尺寸是否符合要求
const selectImageWithSizeCheck = () => {
launchImageLibrary({ mediaType: 'photo' }, (response) => {
if (response.assets?.[0]) {
const { width, height } = response.assets[0];
// 检查最小尺寸
if (width < 800 || height < 600) {
Alert.alert('提示', '图片尺寸太小,请选择更高分辨率的图片');
return;
}
// 检查宽高比
const ratio = width / height;
if (ratio < 0.9 || ratio > 1.1) {
Alert.alert('提示', '请选择接近正方形的图片');
return;
}
setImage(response.assets[0].uri);
}
});
};
// 场景2:根据尺寸计算显示大小
const calculateDisplaySize = (asset: Asset) => {
const maxWidth = 300;
const maxHeight = 300;
let displayWidth = asset.width;
let displayHeight = asset.height;
if (displayWidth > maxWidth) {
displayHeight = (maxWidth / displayWidth) * displayHeight;
displayWidth = maxWidth;
}
if (displayHeight > maxHeight) {
displayWidth = (maxHeight / displayHeight) * displayWidth;
displayHeight = maxHeight;
}
return { width: displayWidth, height: displayHeight };
};
3. fileSize - 文件大小 📊
fileSize 属性表示文件大小(字节)。
typescript
fileSize: number;
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景1:检查文件大小限制
const selectImageWithSizeLimit = () => {
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
launchImageLibrary({ mediaType: 'photo' }, (response) => {
if (response.assets?.[0]) {
const fileSize = response.assets[0].fileSize;
if (fileSize && fileSize > MAX_SIZE) {
Alert.alert('提示', `文件大小 ${(fileSize / 1024 / 1024).toFixed(2)}MB 超过限制 5MB`);
return;
}
uploadImage(response.assets[0]);
}
});
};
// 场景2:显示文件大小
const formatFileSize = (bytes?: number) => {
if (!bytes) return '未知';
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
};
4. type - 文件类型 📄
type 属性表示文件的 MIME 类型。
typescript
type: string;
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景1:根据类型处理不同媒体
const selectAndProcessMedia = () => {
launchImageLibrary({ mediaType: 'mixed' }, (response) => {
if (response.assets?.[0]) {
const asset = response.assets[0];
if (asset.type?.startsWith('image/')) {
console.log('这是图片:', asset.type);
processImage(asset);
} else if (asset.type?.startsWith('video/')) {
console.log('这是视频:', asset.type);
processVideo(asset);
}
}
});
};
// 场景2:上传时设置正确的 Content-Type
const uploadWithCorrectType = (asset: Asset) => {
const formData = new FormData();
formData.append('file', {
uri: asset.uri,
type: asset.type || 'image/jpeg',
name: asset.fileName || 'upload.jpg',
});
};
5. fileName - 文件名称 📝
fileName 属性表示文件的原始名称。
typescript
fileName: string;
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景1:显示文件名
const ImageInfo = () => {
const [selectedFile, setSelectedFile] = useState<string>('');
const selectImage = () => {
launchImageLibrary({ mediaType: 'photo' }, (response) => {
if (response.assets?.[0]) {
setSelectedFile(response.assets[0].fileName || '未知文件');
}
});
};
return (
<View>
<Text>已选择: {selectedFile}</Text>
<Button title="选择图片" onPress={selectImage} />
</View>
);
};
// 场景2:根据文件名判断类型
const getFileExtension = (fileName: string) => {
return fileName.split('.').pop()?.toLowerCase();
};
6. originalPath - 原始路径 📂
originalPath 属性表示文件的原始完整路径。
typescript
originalPath: string;
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景:获取原始路径用于文件操作
const selectAndGetPath = () => {
launchImageLibrary({ mediaType: 'photo' }, (response) => {
if (response.assets?.[0]) {
const originalPath = response.assets[0].originalPath;
console.log('原始路径:', originalPath);
// 可以用于文件复制、移动等操作
}
});
};
7. id - 本地标识符 🆔
id 属性是媒体在设备上的本地标识符。
typescript
id: string;
应用场景:
typescript
import { launchImageLibrary } from 'react-native-image-picker';
// 场景:用于去重或缓存
const selectedIds = new Set<string>();
const selectUniqueImages = () => {
launchImageLibrary({ mediaType: 'photo', selectionLimit: 0 }, (response) => {
if (response.assets) {
const newAssets = response.assets.filter(asset =>
asset.id && !selectedIds.has(asset.id)
);
newAssets.forEach(asset => {
if (asset.id) selectedIds.add(asset.id);
});
console.log(`新增 ${newAssets.length} 张图片`);
}
});
};
🔷 属性支持情况总览
Options 配置属性
| 属性 | 说明 | HarmonyOS 支持 |
|---|---|---|
mediaType |
媒体类型 | ✅ |
cameraType |
相机类型 | ❌ 暂不支持 |
includeBase64 |
Base64 编码 | ✅ |
selectionLimit |
选择数量限制 | ✅ |
maxWidth |
最大宽度 | ❌ |
maxHeight |
最大高度 | ❌ |
quality |
图片质量 | ❌ |
videoQuality |
视频质量 | ❌ |
durationLimit |
视频时长限制 | ❌ |
includeExtra |
额外信息 | ❌ |
saveToPhotos |
保存到相册 | ❌ |
Response 对象属性
| 属性 | 说明 | HarmonyOS 支持 |
|---|---|---|
didCancel |
用户取消 | ✅ |
errorCode |
错误码 | ❌ |
errorMessage |
错误信息 | ❌ |
assets |
媒体数组 | ✅ |
Asset 对象属性
| 属性 | 说明 | HarmonyOS 支持 |
|---|---|---|
uri |
文件路径 | ✅ |
originalPath |
原始路径 | ✅ |
width |
宽度 | ✅ |
height |
高度 | ✅ |
fileSize |
文件大小 | ✅ |
type |
文件类型 | ✅ |
fileName |
文件名 | ✅ |
base64 |
Base64 编码 | ✅ |
id |
本地标识符 | ✅ |
duration |
视频时长 | ❌ |
bitrate |
视频比特率 | ❌ |
timestamp |
时间戳 | ❌ |
💻 完整代码示例

下面是一个完整的示例,展示了 Image Picker 的所有 API 应用场景:
typescript
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
Image,
Alert,
SafeAreaView,
} from 'react-native';
import {
launchImageLibrary,
ImagePickerResponse,
Asset,
} from 'react-native-image-picker';
function ImagePickerDemo() {
const [selectedImages, setSelectedImages] = useState<Asset[]>([]);
const [lastResponse, setLastResponse] = useState<string>('');
// 格式化文件大小
const formatFileSize = (bytes?: number) => {
if (!bytes) return '未知';
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
};
// 处理响应
const handleResponse = (response: ImagePickerResponse) => {
setLastResponse(JSON.stringify(response, null, 2));
if (response.didCancel) {
console.log('用户取消了选择');
return;
}
if (response.errorCode) {
Alert.alert('错误', response.errorMessage || '选择图片失败');
return;
}
if (response.assets && response.assets.length > 0) {
setSelectedImages(response.assets);
}
};
// 1. 单选图片
const selectSingleImage = () => {
launchImageLibrary(
{
mediaType: 'photo',
selectionLimit: 1,
},
handleResponse
);
};
// 2. 多选图片(最多9张)
const selectMultipleImages = () => {
launchImageLibrary(
{
mediaType: 'photo',
selectionLimit: 9,
},
handleResponse
);
};
// 3. 选择图片并获取Base64
const selectWithBase64 = () => {
launchImageLibrary(
{
mediaType: 'photo',
selectionLimit: 1,
includeBase64: true,
},
(response) => {
if (response.assets?.[0]) {
const asset = response.assets[0];
console.log('Base64长度:', asset.base64?.length);
handleResponse(response);
}
}
);
};
// 4. 选择视频
const selectVideo = () => {
launchImageLibrary(
{
mediaType: 'video',
selectionLimit: 1,
},
handleResponse
);
};
// 5. 选择图片或视频
const selectMixed = () => {
launchImageLibrary(
{
mediaType: 'mixed',
selectionLimit: 5,
},
handleResponse
);
};
// 6. 无限制选择
const selectUnlimited = () => {
launchImageLibrary(
{
mediaType: 'photo',
selectionLimit: 0,
},
handleResponse
);
};
// 清空选择
const clearSelection = () => {
setSelectedImages([]);
setLastResponse('');
};
return (
<SafeAreaView style={styles.container}>
<ScrollView style={styles.scrollView}>
<Text style={styles.title}>📷 Image Picker 演示</Text>
{/* 功能按钮区 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>相册选择</Text>
<View style={styles.buttonRow}>
<TouchableOpacity style={styles.button} onPress={selectSingleImage}>
<Text style={styles.buttonText}>单选图片</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={selectMultipleImages}>
<Text style={styles.buttonText}>多选(最多9张)</Text>
</TouchableOpacity>
</View>
<View style={styles.buttonRow}>
<TouchableOpacity style={styles.button} onPress={selectWithBase64}>
<Text style={styles.buttonText}>获取Base64</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={selectUnlimited}>
<Text style={styles.buttonText}>无限制选择</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>媒体类型</Text>
<View style={styles.buttonRow}>
<TouchableOpacity style={[styles.button, styles.buttonVideo]} onPress={selectVideo}>
<Text style={styles.buttonText}>选择视频</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.buttonMixed]} onPress={selectMixed}>
<Text style={styles.buttonText}>图片+视频</Text>
</TouchableOpacity>
</View>
</View>
{/* 已选图片展示 */}
{selectedImages.length > 0 && (
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>已选择 {selectedImages.length} 张</Text>
<TouchableOpacity onPress={clearSelection}>
<Text style={styles.clearText}>清空</Text>
</TouchableOpacity>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{selectedImages.map((asset, index) => (
<View key={`image-${index}-${asset.uri}`} style={styles.imageCard}>
<Image
source={{ uri: asset.uri }}
style={styles.thumbnail}
resizeMode="cover"
/>
<View style={styles.imageInfo}>
<Text style={styles.imageName} numberOfLines={1}>
{asset.fileName}
</Text>
<Text style={styles.imageSize}>
{asset.width} × {asset.height}
</Text>
<Text style={styles.imageSize}>
{formatFileSize(asset.fileSize)}
</Text>
<Text style={styles.imageType}>
{asset.type}
</Text>
</View>
</View>
))}
</ScrollView>
</View>
)}
{/* 响应数据 */}
{lastResponse ? (
<View style={styles.section}>
<Text style={styles.sectionTitle}>响应数据</Text>
<ScrollView style={styles.responseBox}>
<Text style={styles.responseText}>{lastResponse}</Text>
</ScrollView>
</View>
) : null}
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
scrollView: {
flex: 1,
padding: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
color: '#333',
},
section: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
sectionTitle: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 12,
},
clearText: {
color: '#FF3B30',
fontSize: 14,
},
buttonRow: {
flexDirection: 'row',
gap: 10,
marginBottom: 10,
},
button: {
flex: 1,
backgroundColor: '#007AFF',
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
},
buttonVideo: {
backgroundColor: '#34C759',
},
buttonMixed: {
backgroundColor: '#FF9500',
},
buttonText: {
color: '#fff',
fontSize: 14,
fontWeight: '600',
},
imageCard: {
width: 160,
marginRight: 12,
backgroundColor: '#f8f9fa',
borderRadius: 8,
overflow: 'hidden',
},
thumbnail: {
width: '100%',
height: 120,
},
imageInfo: {
padding: 8,
},
imageName: {
fontSize: 12,
fontWeight: '500',
color: '#333',
},
imageSize: {
fontSize: 11,
color: '#666',
marginTop: 2,
},
imageType: {
fontSize: 10,
color: '#999',
marginTop: 2,
},
responseBox: {
backgroundColor: '#f0f0f0',
borderRadius: 8,
padding: 12,
maxHeight: 200,
},
responseText: {
fontSize: 11,
fontFamily: 'monospace',
color: '#333',
},
});
export default ImagePickerDemo;
⚠️ 注意事项与最佳实践
1. 最佳实践
typescript
// ✅ 推荐:处理所有可能的响应情况
const selectImageSafely = () => {
launchImageLibrary({ mediaType: 'photo' }, (response) => {
if (response.didCancel) {
return;
}
if (response.errorCode) {
Alert.alert('错误', response.errorMessage || '未知错误');
return;
}
if (response.assets?.[0]) {
processImage(response.assets[0]);
}
});
};
// ✅ 推荐:检查文件大小后再上传
const checkAndUpload = (asset: Asset) => {
const MAX_SIZE = 10 * 1024 * 1024; // 10MB
if (asset.fileSize && asset.fileSize > MAX_SIZE) {
Alert.alert('提示', '文件过大,请选择较小的图片');
return;
}
uploadImage(asset);
};
// ⚠️ 注意:大图片使用 Base64 会影响性能
// 仅在必要时开启 includeBase64
2. 遗留问题
| 问题 | 状态 | Issue |
|---|---|---|
| 部分属性未实现 HarmonyOS 化 | ⚠️ 待修复 | issue#14 |
| 相机功能暂未适配 | ⚠️ 待修复 | issue#13 |
🧪 测试验证
1. 测试要点
-
单选模式: 确认只能选择一张图片
-
多选模式 : 确认可以选择多张图片

-
媒体类型: 测试 photo、video、mixed 三种类型
-
Base64 : 确认 Base64 编码正确返回

-
文件信息: 验证 width、height、fileSize 等属性
⚠️ 注意: 相机功能暂未适配,无法测试拍照功能。
2. 常见问题排查
问题 1: 无法打开相册
- 检查是否配置了 READ_MEDIA 权限
- 确认原生代码是否正确链接
- 查看控制台错误日志
问题 2: 图片无法显示
- 检查 uri 是否正确获取
- 确认 Image 组件的 source 格式正确
- 尝试使用 originalPath 替代 uri
问题 3: Base64 返回为空
- 确认 includeBase64 设置为 true
- 检查图片是否过大导致内存问题
⚠️ 重要提示: 相机功能(launchCamera)暂未适配 HarmonyOS,目前仅支持相册选择功能。