高级进阶 ReactNative for Harmony项目鸿蒙化三方库集成实战:react-native-image-picker(打开手机相册)
是 React Native 社区最流行的图片选择库,提供了一套完整的图片和视频选择解决方案。它支持从相册选择、相机拍照、视频录制等多种媒体获取方式,提供了丰富的配置选项和回调处理,并且完全兼容 Android、iOS 和 HarmonyOS 三端。库名称版本信息7.0.4: 支持 RN 0.72 版本8.2.2: 支持 RN 0.77 版本官方仓库主要功能从相册选择图片/视频拍照功能(暂未适配)

📋 前言
react-native-image-picker 是 React Native 社区最流行的图片选择库,提供了一套完整的图片和视频选择解决方案。它支持从相册选择、相机拍照、视频录制等多种媒体获取方式,提供了丰富的配置选项和回调处理,并且完全兼容 Android、iOS 和 HarmonyOS 三端。
内容由熊哈哈O_o大佬指导创作:https://blog.csdn.net/qq_61024956?type=blog
🎯 库简介
基本信息
- 库名称: @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
- 主要功能:
- 从相册选择图片/视频
- 拍照功能(暂未适配)
- 支持多选(最多50张)
- 支持图片压缩
- 支持获取图片元数据
- 兼容 Android、iOS 和 HarmonyOS
- 兼容性验证:
- RNOH: 0.72.96; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
- RNOH: 0.72.33; SDK: HarmonyOS NEXT B1; IDE: DevEco Studio: 5.0.3.900; ROM: Next.0.0.71;
- RNOH: 0.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
为什么需要这个库?
- 功能完整: 提供完整的图片选择解决方案
- 跨平台: 在三端提供一致的体验
- 易于集成: API 简单直观
- 灵活配置: 支持多种配置选项
- 性能优异: 原生实现,高效稳定
📦 安装步骤
1. 使用 npm 安装
根据您的 RN 版本选择对应的包名:
npm install @react-native-ohos/react-native-image-picker@7.0.4-rc.1
2. 验证安装
安装完成后,检查 package.json 文件,应该能看到新增的依赖:
{
"dependencies": {
"@react-native-ohos/react-native-image-picker": "^7.0.4",
// ... 其他依赖
}
}
🔧 HarmonyOS 平台配置 ⭐
由于 HarmonyOS 暂不支持 AutoLink,需要手动配置原生端代码。本文采用方法一:通过 har 包引入的方式。
1. 在工程根目录的 oh-package.json5 添加 overrides 字段
首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 harmony
打开 harmony/oh-package.json5,添加以下配置:
{
...
"overrides": {
"@rnoh/react-native-openharmony": "^0.72.90"
}
}
2. 引入原生端代码
方法一:通过 har 包引入(不推荐)
[!TIP] har 包位于三方库安装路径的
harmony文件夹下。
打开 entry/oh-package.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"
}
点击右上角的 sync 按钮
或者在终端执行:
cd entry
ohpm install
方法二:直接链接源码(我用的这个)
目前 DevEco Studio 不支持通过源码引入外部 module,我们推荐使用直接链接源码的方式,将源码通过操作改成 harmony 工程的内部模块。
步骤 1: 把 <RN工程>/node_modules/@react-native-ohos/react-native-image-picker/harmony 目录下的源码 image_picker 复制到 harmony(鸿蒙壳工程)工程根目录下。
步骤 2: 在 harmony 工程根目录的 build-profile.template.json5(若存在)和 build-profile.json5 添加以下模块:
modules: [
...
{
name: '<xxx>',
srcPath: './<xxx>',
},
{
name: 'image_picker',
srcPath: './image_picker',
}
]
步骤 3: 打开 image_picker/oh-package.json5,修改 react-native-openharmony 和项目的版本一致。
步骤 4: 打开 entry/oh-package.json5,添加以下依赖:
"dependencies": {
"@rnoh/react-native-openharmony": "0.72.90",
"@react-native-ohos/react-native-image-picker": "file:../image_picker"
}
步骤 5: 点击 DevEco Studio 右上角的 sync 按钮
3. 配置 CMakeLists 和引入 ImagePickerViewPackage
若使用的是 <= 7.0.3-0.1.8 版本,请跳过本章。
打开 entry/src/main/cpp/CMakeLists.txt,添加:
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,noexecstack,-s -fPIE -pie")
set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
add_compile_definitions(WITH_HITRACE_SYSTRACE)
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
# RNOH_BEGIN: manual_package_linking_1
add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package)
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-image-picker/src/main/cpp" ./image_picker)
# RNOH_END: manual_package_linking_1
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)
# RNOH_BEGIN: manual_package_linking_2
target_link_libraries(rnoh_app PUBLIC rnoh_sample_package)
+ target_link_libraries(rnoh_app PUBLIC rnoh_image_picker)
# RNOH_END: manual_package_linking_2
打开 entry/src/main/cpp/PackageProvider.cpp,添加:
#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
#include "SamplePackage.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<SamplePackage>(ctx),
+ std::make_shared<RNImagePickerPackage>(ctx)
};
}
4. 在 ArkTs 侧引入 ImagePickerViewPackage
打开 entry/src/main/ets/RNPackagesFactory.ts,添加:
+ import { ImagePickerViewPackage } from '@react-native-ohos/react-native-image-picker/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
new SamplePackage(ctx),
+ new ImagePickerViewPackage(ctx)
];
}
5. 运行
点击右上角的 sync 按钮
或者在终端执行:
cd entry
ohpm install
然后编译、运行即可。
💻 完整代码示例
下面是一个完整的示例,展示了 react-native-image-picker 的各种使用场景:
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
ScrollView,
Alert,
} from 'react-native';
import {
launchImageLibrary,
launchCamera,
type ImagePickerResponse,
type Asset,
type MediaType,
} from 'react-native-image-picker';
function ImagePickerDemo() {
const [selectedImages, setSelectedImages] = useState<Asset[]>([]);
const [selectedImage, setSelectedImage] = useState<Asset | null>(null);
const [mediaType, setMediaType] = useState<MediaType>('photo');
const [selectionLimit, setSelectionLimit] = useState<number>(1);
const [includeBase64, setIncludeBase64] = useState<boolean>(false);
const [cameraType, setCameraType] = useState<'back' | 'front'>('back');
// 从相册选择图片/视频
const pickFromLibrary = () => {
launchImageLibrary(
{
mediaType: mediaType,
selectionLimit: selectionLimit,
includeBase64: includeBase64,
},
(response: ImagePickerResponse) => {
if (response.didCancel) {
Alert.alert('取消', '用户取消了选择');
} else if (response.errorMessage) {
Alert.alert('错误', response.errorMessage);
} else if (response.assets) {
if (selectionLimit === 1) {
setSelectedImage(response.assets[0]);
} else {
setSelectedImages(response.assets);
}
}
}
);
};
// 打开相机拍照
const openCamera = () => {
launchCamera(
{
mediaType: mediaType,
cameraType: cameraType,
includeBase64: includeBase64,
},
(response: ImagePickerResponse) => {
if (response.didCancel) {
Alert.alert('取消', '用户取消了拍照');
} else if (response.errorMessage) {
Alert.alert('错误', response.errorMessage);
} else if (response.errorMessage) {
Alert.alert('错误', '相机功能暂未适配 HarmonyOS');
} else if (response.assets) {
setSelectedImage(response.assets[0]);
}
}
);
};
// 清空选择
const clearSelection = () => {
setSelectedImage(null);
setSelectedImages([]);
};
// 切换媒体类型
const changeMediaType = (type: MediaType) => {
setMediaType(type);
};
// 切换选择数量
const toggleMultiSelect = () => {
setSelectionLimit(selectionLimit === 1 ? 0 : 1);
};
// 切换相机方向
const toggleCameraType = () => {
setCameraType(cameraType === 'back' ? 'front' : 'back');
};
// 切换 Base64
const toggleBase64 = () => {
setIncludeBase64(!includeBase64);
};
return (
<ScrollView style={styles.container}>
<View style={styles.content}>
<Text style={styles.title}>图片选择器示例</Text>
{/* 单选结果展示 */}
{selectedImage && (
<View style={styles.resultSection}>
<Text style={styles.sectionTitle}>单选结果</Text>
<Image
source={{ uri: selectedImage.uri }}
style={styles.image}
/>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>文件名:</Text>
<Text style={styles.infoText}>{selectedImage.fileName}</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>尺寸:</Text>
<Text style={styles.infoText}>
{selectedImage.width} x {selectedImage.height}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>文件大小:</Text>
<Text style={styles.infoText}>
{(selectedImage.fileSize! / 1024).toFixed(2)} KB
</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>类型:</Text>
<Text style={styles.infoText}>{selectedImage.type}</Text>
</View>
{selectedImage.base64 && (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>Base64:</Text>
<Text style={styles.infoText} numberOfLines={1}>
{selectedImage.base64.substring(0, 50)}...
</Text>
</View>
)}
</View>
)}
{/* 多选结果展示 */}
{selectedImages.length > 0 && (
<View style={styles.resultSection}>
<Text style={styles.sectionTitle}>多选结果 ({selectedImages.length})</Text>
<View style={styles.imageGrid}>
{selectedImages.map((asset, index) => (
<Image
key={index}
source={{ uri: asset.uri }}
style={styles.gridImage}
/>
))}
</View>
</View>
)}
{/* 媒体类型选择 */}
<Text style={styles.sectionTitle}>媒体类型</Text>
<View style={styles.buttonRow}>
{['photo', 'video', 'mixed'].map((type) => (
<TouchableOpacity
key={type}
style={[
styles.button,
mediaType === type && styles.buttonActive,
]}
onPress={() => changeMediaType(type as MediaType)}
>
<Text style={styles.buttonText}>
{type === 'photo' ? '图片' : type === 'video' ? '视频' : '混合'}
</Text>
</TouchableOpacity>
))}
</View>
{/* 多选开关 */}
<Text style={styles.sectionTitle}>多选模式</Text>
<TouchableOpacity
style={[
styles.button,
selectionLimit === 0 && styles.buttonActive,
]}
onPress={toggleMultiSelect}
>
<Text style={styles.buttonText}>
{selectionLimit === 1 ? '单选' : '多选'}
</Text>
</TouchableOpacity>
{/* 相机方向 */}
<Text style={styles.sectionTitle}>相机方向</Text>
<TouchableOpacity
style={styles.button}
onPress={toggleCameraType}
>
<Text style={styles.buttonText}>
{cameraType === 'back' ? '后置' : '前置'}
</Text>
</TouchableOpacity>
{/* Base64 开关 */}
<Text style={styles.sectionTitle}>包含 Base64</Text>
<TouchableOpacity
style={[
styles.button,
includeBase64 && styles.buttonActive,
]}
onPress={toggleBase64}
>
<Text style={styles.buttonText}>
{includeBase64 ? '开启' : '关闭'}
</Text>
</TouchableOpacity>
{/* 操作按钮 */}
<Text style={styles.sectionTitle}>操作</Text>
<View style={styles.buttonRow}>
<TouchableOpacity
style={[styles.button, styles.buttonPrimary]}
onPress={pickFromLibrary}
>
<Text style={styles.buttonText}>从相册选择</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.buttonWarning]}
onPress={openCamera}
>
<Text style={styles.buttonText}>打开相机</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.buttonDanger]}
onPress={clearSelection}
>
<Text style={styles.buttonText}>清空</Text>
</TouchableOpacity>
</View>
{/* 说明 */}
<View style={styles.noteSection}>
<Text style={styles.noteTitle}>注意事项:</Text>
<Text style={styles.noteText}>
• 相机拍照功能暂未适配 HarmonyOS
</Text>
<Text style={styles.noteText}>
• 多选模式最多支持选择 50 张图片
</Text>
<Text style={styles.noteText}>
• 包含 Base64 会增加内存占用,谨慎使用
</Text>
<Text style={styles.noteText}>
• 需要在应用权限中申请读取相册权限
</Text>
</View>
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
content: {
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
textAlign: 'center',
color: '#333',
},
resultSection: {
marginBottom: 20,
padding: 15,
backgroundColor: '#fff',
borderRadius: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
sectionTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 10,
marginTop: 20,
color: '#333',
},
image: {
width: '100%',
height: 200,
resizeMode: 'contain',
marginBottom: 10,
},
infoRow: {
flexDirection: 'row',
marginBottom: 5,
},
infoLabel: {
fontSize: 14,
fontWeight: 'bold',
color: '#666',
width: 80,
},
infoText: {
fontSize: 14,
color: '#333',
flex: 1,
},
imageGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 10,
},
gridImage: {
width: 80,
height: 80,
borderRadius: 8,
},
buttonRow: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 10,
marginBottom: 10,
},
button: {
paddingHorizontal: 15,
paddingVertical: 12,
backgroundColor: '#42a5f5',
borderRadius: 8,
minWidth: 100,
},
buttonActive: {
backgroundColor: '#66bb6a',
},
buttonPrimary: {
backgroundColor: '#42a5f5',
},
buttonWarning: {
backgroundColor: '#ffa726',
},
buttonDanger: {
backgroundColor: '#ef5350',
},
buttonText: {
color: '#fff',
fontSize: 14,
fontWeight: '500',
textAlign: 'center',
},
noteSection: {
marginTop: 20,
padding: 15,
backgroundColor: '#fff3cd',
borderRadius: 8,
borderLeftWidth: 4,
borderLeftColor: '#ffc107',
},
noteTitle: {
fontSize: 14,
fontWeight: 'bold',
marginBottom: 8,
color: '#856404',
},
noteText: {
fontSize: 13,
color: '#856404',
marginBottom: 4,
},
});
export default ImagePickerDemo;
🎨 实际应用场景
react-native-image-picker 可以应用于以下实际场景:
- 用户头像设置: 从相册选择或拍照上传用户头像
- 图片上传: 发布动态、商品图片等
- 视频上传: 发布短视频、教程视频等
- 证件上传: 身份证、银行卡等证件照片
- 相册管理: 批量选择图片进行编辑或分享
⚠️ 注意事项与最佳实践
1. 权限配置
在使用 image-picker 之前,需要在 HarmonyOS 的 module.json5 中配置权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.READ_IMAGEVIDEO",
"reason": "$string:read_imagevideo_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
2. 错误处理
launchImageLibrary(options, (response) => {
if (response.didCancel) {
// 用户取消
return;
}
if (response.errorMessage) {
// 处理错误
Alert.alert('错误', response.errorMessage);
return;
}
if (response.assets) {
// 处理选中的图片
}
});
3. 性能优化
- 避免选择过多图片(建议不超过 10 张)
- 关闭
includeBase64选项,除非确实需要 - 使用
selectionLimit限制选择数量 - 对大图片进行压缩处理
4. HarmonyOS 特殊处理
在 HarmonyOS 上,需要注意:
- 相机拍照功能暂未适配
- 多选模式最多支持 50 张图片
- 需要手动配置权限
- 文件 URI 格式可能与其他平台不同
5. 最佳实践
// ✅ 推荐:使用类型安全
import type { ImagePickerResponse, Asset } from 'react-native-image-picker';
// ✅ 推荐:添加错误处理
try {
const response = await launchImageLibrary(options);
if (response.assets) {
// 处理图片
}
} catch (error) {
console.error('图片选择失败:', error);
}
// ✅ 推荐:限制选择数量
launchImageLibrary({
mediaType: 'photo',
selectionLimit: 5, // 最多选择 5 张
});
// ❌ 不推荐:不设置 selectionLimit
launchImageLibrary({
mediaType: 'photo',
// selectionLimit 未设置,默认为 1
});
🧪 测试验证
1. Android 平台测试
npm run android
测试要点:
- 测试从相册选择图片
- 测试多选功能
- 测试视频选择
- 验证图片元数据
2. iOS 平台测试
npm run ios
测试要点:
- 测试从相册选择图片
- 测试相机拍照
- 测试多选功能
- 验证图片质量
3. HarmonyOS 平台测试
npm run harmony
测试要点:
- 验证图片选择功能
- 测试多选功能
- 检查权限请求
- 验证图片显示
4. 常见问题排查
问题 1: 无法打开相册
- 检查权限配置
- 确认权限已授予
- 验证包名配置
问题 2: 图片显示失败
- 检查 URI 格式
- 确认文件存在
- 验证 Image 组件配置
问题 3: 多选功能不工作
- 检查
selectionLimit配置 - 确认 HarmonyOS 版本支持
- 验证相册权限
📊 对比:原生选择器 vs react-native-image-picker
| 特性 | 原生选择器 | react-native-image-picker |
|---|---|---|
| 跨平台一致性 | ❌ | ✅ 完全一致 |
| API 简洁性 | ⚠️ 复杂 | ✅ 简洁 |
| 功能丰富度 | ✅ 完整 | ✅ 完整 |
| 多选支持 | ✅ 支持 | ✅ 支持(最多50张) |
| 图片压缩 | ✅ 支持 | ⚠️ 部分 |
| 视频支持 | ✅ 支持 | ✅ 支持 |
| 自定义能力 | ⚠️ 受限 | ✅ 灵活 |
📝 总结
通过集成 react-native-image-picker,我们为项目添加了强大的图片选择能力。这个库提供了完整的图片和视频选择解决方案,支持多种选择方式和配置选项,并且完全跨平台兼容。
关键要点回顾
- ✅ 安装依赖:
npm install @react-native-ohos/react-native-image-picker - ✅ 配置平台: 通过 har 包或直接链接源码,配置 CMakeLists.txt、PackageProvider.cpp、RNPackagesFactory.ts
- ✅ 集成代码: 使用
launchImageLibrary和launchCamera方法 - ✅ 支持功能: 相册选择、多选、图片元数据、视频选择等
- ✅ 重要: 相机拍照功能暂未适配 HarmonyOS
实际效果
- Android: 原生相册选择体验
- iOS: 高质量的图片选择
- HarmonyOS: 一致的选择体验
已知限制
- ❌ 相机拍照功能暂未适配 HarmonyOS
- ⚠️ 部分高级配置项未实现
- ⚠️ 图片压缩功能有限
希望这篇教程能帮助你顺利集成 react-native-image-picker,构建出色的图片选择体验!
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)