rntpc_react-native-image-picker:基于 react-native-image-picker 的 OpenHarmony 适配版,用于从相册选择或相机拍摄图片/视频

基于 react-native-image-picker 的 OpenHarmony 适配版,用于从相册选择或相机拍摄图片/视频

分支5Tags6

文档模板:v0.4.2

react-native-image-picker

本项目基于 react-native-image-picker 开发。

该第三方库的仓库已迁移至 Gitcode,且支持直接从 npm 下载,新的包名为:@react-native-ohos/react-native-image-picker 版本所属关系如下:

三方库名称 三方库版本(npm地址) 发布信息 支持RN版本 Autolink 编译API版本 社区基线版本 源码地址
@react-native-ohos/react-native-image-picker ~ 8.3.0(开发中) Gitcode Releases 0.77.* / 0.82.* / 0.84.* partially(0.82/0.84) API12+ 8.2.1 matser
@react-native-ohos/react-native-image-picker 8.2.2 Gitcode Releases 0.77.* 否 API12+ 8.2.1 br_rnoh0.77
@react-native-ohos/react-native-image-picker ~ 7.0.4 Gitcode Releases 0.72.* 是 API12+ 7.0.3 br_rnoh0.72
@react-native-oh-tpl/react-native-image-picker <=7.0.3-0.1.8(@deprecated) Github Releases(deprecated) 0.72.* 否 API12+ 7.0.0 sig

简介

react-native-image-picker是一个可让你从设备图库或相机中选择照片/视频的库。

下载安装

进入到工程目录并输入以下命令:

npm

npm install @react-native-ohos/react-native-image-picker

yarn

yarn add @react-native-ohos/react-native-image-picker
是否支持autolink RN框架版本
~8.3.0 partially(0.82/0.84) 0.77/0.82/0.84
8.2.2 No 0.77

使用AutoLink的工程需要根据该文档配置,Autolink框架指导文档:https://gitcode.com/CPF-RN/ohos_react_native/blob/master/docs/zh-cn/Autolinking.md

如您使用的版本支持 Autolink,并且工程已接入 Autolink,可跳过ManualLink配置。

ManualLink: 此步骤为手动配置原生依赖项的指导

首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 harmony。

1. Overrides RN SDK

为了让工程依赖同一个版本的 RN SDK,需要在工程根目录的 oh-package.json5 添加 overrides 字段,指向工程需要使用的 RN SDK 版本。替换的版本既可以是一个具体的版本号,也可以是一个模糊版本,还可以是本地存在的 HAR 包或源码目录。

关于该字段的作用请阅读官方说明

{
  "overrides": {
    "@rnoh/react-native-openharmony": "^0.82.25" // ohpm 在线版本
    // "@rnoh/react-native-openharmony" : "./react_native_openharmony.har" // 指向本地 har 包的路径
    // "@rnoh/react-native-openharmony" : "./react_native_openharmony" // 指向源码路径
  }
}

2. 引入原生端代码

目前有两种方法:

  • 通过 har 包引入;
  • 直接链接源码。

方法一:通过 har 包引入(推荐)

har 包位于三方库安装路径的 `harmony` 文件夹下。

打开 entry/oh-package.json5,添加以下依赖

"dependencies": {
  "@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

方法二:直接链接源码

如需使用直接链接源码,请参考[直接链接源码说明](https://gitcode.com/CPF-RN/usage-docs/blob/master/zh-cn/link-source-code.md)

3. 配置 CMakeLists 和引入 ImagePickerViewPackage

打开 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,now,-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 "SamplePackage.h"
+ #include "RNImagePickerPackage.h"

using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
      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 ImagePickerViewPackage(ctx)
  ];
}

运行

点击右上角的 sync 按钮

或者在命令行终端执行:

cd entry
ohpm install

然后编译、运行即可。

约束与限制

兼容性

本文档内容基于以下版本验证通过:

  1. RNOH: 0.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
  2. RNOH: 0.82.25; SDK: HarmonyOS 6.0.1 Release SDK; IDE: DevEco Studio 6.0.1 Release; ROM:6.0.0.120 SP7;
  3. RNOH: 0.84.1; SDK: HarmonyOS 6.0.1 Release SDK; IDE: DevEco Studio 6.0.1 Release; ROM:6.0.0.120 SP7;

权限要求

在 entry 目录下的 module.json5 中添加权限

"requestPermissions": [
+  {
+    "name": "ohos.permission.CAMERA",  // 相机权限名称
+    "reason": "$string:camera_reason",
+    "usedScene": {
+      "abilities": [
+        "EntryAbility"
+      ],
+      "when":"inuse"
+    }
+  },
+  {
+    "name": "ohos.permission.MICROPHONE", // 麦克风权限名称
+    "reason": "$string:mic_reason",
+    "usedScene": {
+      "abilities": [
+        "EntryAbility"
+      ],
+      "when":"inuse"
+    }
+  },
]

在 entry 目录下添加申请以上权限的原因 打开 entry/src/main/resources/base/element/string.json,添加:

{
  "string": [
+    {
+      "name": "microphone_reason",
+      "value": "使用麦克风"
+    },
+    {
+      "name": "camera_reason",
+      "value": "使用相机"
+    },
  ]
}

使用示例

下面的代码展示了这个库的基本使用场景:

使用时 import 的库名不变。

import React from 'react';
import { View, Text } from 'react-native';
import { launchImageLibrary } from 'react-native-image-picker';

const App = () => {
  const [urlInfo, setUrlInfo] = React.useState<string>();
  return (
    <View style={{ flex: 1, marginTop: 100 }}>
      <View style={{
        width: 160,
        height: 36,
        backgroundColor: 'hsl(190,50%,70%)',
        paddingHorizontal: 16,
        paddingVertical: 8,
        borderRadius: 8
      }} onTouchEnd={() => {
        launchImageLibrary({ mediaType: 'photo', selectionLimit: 1 }, (data) => {
          if (data.assets?.length) {
            setUrlInfo(JSON.stringify(data.assets))
          }
        })
      }}>
        <Text style={{ width: '100%', height: '100%', fontWeight: 'bold', textAlign: 'center' }}>选择图片</Text>
      </View>
      <Text>{urlInfo}</Text>
    </View>
  );
};

export default App;

使用说明

launchImageLibrary(启动相册选择图片或视频)

import { launchImageLibrary } from 'react-native-image-picker';

launchImageLibrary({
    mediaType: 'photo',          // 'photo' | 'video' | 'mixed'
    selectionLimit: 1,           // 可选数量,0 
    quality: 1,                  // 压缩质量 0~1,1 为不压缩
    includeBase64: false,        // 是否包含 base64 字符串
    restrictMimeTypes: ['image/jpeg', 'image/png'],  // 限制可选 MIME 类型
    maxWidth: 800,            // 调整图片宽度
    maxHeight: 600,           // 调整图片高度
    durationLimit: 60,        // 视频最大录制时长,秒
    includeExtra: true,       // 是否包含 exif 等额外数据
}, (data) => {
    if (data.didCancel) {
        console.log('用户取消选择');
        return;
    }
    if (data.errorCode) {
        console.log('错误:', data.errorCode, data.errorMessage);
        return;
    }
    data.assets?.forEach((asset) => {
        console.log('uri:', asset.uri);
        console.log('fileName:', asset.fileName);
        console.log('fileSize:', asset.fileSize);
        console.log('width:', asset.width, 'height:', asset.height);
        console.log('type:', asset.type);
    });
});

launchCamera(启动相机拍摄照片或视频)

import { launchCamera } from 'react-native-image-picker';

launchCamera({
    mediaType: 'photo',          // 'photo' | 'video'
    cameraType: 'back',          // 'back' | 'front'
    quality: 1,                  // 压缩质量 0~1
    includeBase64: false,        // 是否包含 base64 字符串
    maxWidth: 800,            // 调整图片宽度
    maxHeight: 600,           // 调整图片高度
    durationLimit: 60,        // 视频最大录制时长,秒
    includeExtra: true,       // 是否包含 exif 等额外数据
}, (data) => {
    if (data.didCancel) {
        console.log('用户取消拍摄');
        return;
    }
    if (data.errorCode) {
        console.log('错误:', data.errorCode, data.errorMessage);
        return;
    }
    if (data.assets?.length) {
        console.log('uri:', data.assets[0].uri);
        console.log('fileName:', data.assets[0].fileName);
        console.log('fileSize:', data.assets[0].fileSize);
        console.log('width:', data.assets[0].width, 'height:', data.assets[0].height);
    }
});

接口说明

"Platform"列表示该属性在原三方库上支持的平台。

"OpenHarmony Support"列为 yes 表示 OpenHarmony平台支持 该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。

API

Name Description Type Required Platform OpenHarmony Support
launchCamera 启动相机拍摄照片或视频。 function yes iOS Android Web yes
launchImageLibrary 启动相册选择图片或视频。 function yes iOS Android Web yes

属性

Options

Name Description Type Required Platform OpenHarmony Support
mediaType 筛选资源类型,photo、video 或 mixed(launchCamera 在 Android 上不支持 'mixed')。Web 目前仅支持 'photo'。 'photo' | 'video' | 'mixed' yes iOS Android Web yes
restrictMimeTypes8.2.2+ 包含允许选取的 MIME 类型的数组。默认值为空(需要API19+支持)。 string[] no Android yes
maxWidth 调整图片宽度。 number no iOS Android yes
maxHeight 调整图片高度。 number no iOS Android yes
videoQuality 视频拍摄的质量,iOS 上为 low、medium 或 high,Android 上为 low 或 high。 string no iOS Android no
durationLimit 视频最大录制时长(秒)。 number no iOS Android yes
quality 0 到 1,用于照片压缩,1 为不压缩。 0 | 0.1 | 0.2 | 0.3 | 0.4 | 0.5 | 0.6 | 0.7 | 0.8 | 0.9 | 1.0 no iOS Android yes
cameraType 拉起相机时摄像头类型,'back' 代表后置 或 'front'代表前置(少数 Android 设备可能不支持)。 string no iOS Android yes
includeBase64 如果为 true,生成图片的 base64 字符串(出于性能考虑,避免在大图片文件上使用)。 boolean no iOS Android Web yes
includeExtra 如果为 true,将包含需要请求库权限的额外数据(如 exif 数据)。 boolean no iOS Android yes
saveToPhotos (布尔值)仅用于 launchCamera,将拍摄的图片/视频文件保存到公共相册。 boolean no iOS Android no
selectionLimit 支持任意整数值。使用 0 表示在 iOS >= 14 和 Android >= 13 上允许任意数量文件,在 HarmonyOS 上,0 表示最多允许 50 个文件。默认为 1。 number no iOS Android Web yes
presentationStyle 控制选择器的展示方式。可选值:currentContext、pageSheet、fullScreen、formSheet、popover、overFullScreen、overCurrentContext。默认为 currentContext。 string no iOS no
formatAsMp4 将选中的视频转换为 MP4 格式(仅 iOS)。 boolean no iOS no
assetRepresentationMode 当资源包含多个 表示形式时,决定使用哪个表示形式的模式。可选值:'auto'、'current'、'compatible'。默认为 'auto'。 string no iOS no

The Response Object

Name Description Type Required Platform HarmonyOS Support
didCancel 如果用户取消了操作,则为 true boolean no iOS Android Web yes
errorCode 查看 ErrorCode 获取所有错误码(camera_unavailable | permission | others) string no iOS Android Web no
errorMessage 错误描述,仅用于调试目的 string no iOS Android Web no
assets 选中媒体的数组,参见 Asset 对象 Asset no iOS Android Web yes

Asset Object

Name Description Type Required Platform OpenHarmony Support
base64 图片的 base64 字符串(仅照片) string no iOS Android Web yes
uri 应用缓存存储中的文件 URI。Android 相册选择视频时除外,此时会获得只读的 content uri,
需要通过 react-native 库将文件复制到应用存储中以获取文件 URI。Web 端使用 base64 作为 URI。
string yes iOS Android Web yes
originalPath 原始文件路径。 string yes iOS Android Web yes
width 资源的宽度 number yes iOS Android Web yes
height 资源的高度 number yes iOS Android Web yes
fileSize 文件大小 number yes iOS Android yes
type 文件类型 string yes iOS Android yes
fileName 文件名 string yes iOS Android yes
duration 选中视频的时长(秒) number no iOS Android yes
bitrate 选中视频的平均比特率(bits/sec),需要API20+才支持。 number no Android yes
timestamp 资源的时间戳。仅当 'includeExtra' 为 true 时包含 string no iOS Android yes
id 照片或视频的本地标识符。在 Android 和 HarmonyOS 上,该值与 originalPath 相同 string no iOS Android yes

遗留问题

其他

quality 属性在小于1的时候压缩后的图片大小会大于原图,属于正常现象,quality 受原图已高度压缩、图片内容复杂度、元数据影响、编码器差异多重因素影响。

quality 属性不传递或者传递1的时候,默认不做压缩处理,直接将图库的图片copy一份到沙箱路径然后返回,目前heic、heif 格式的图片,当在图库中选中图片后图库会对这两个格式的图片进行转码保存到一个重定向的uri返回给用户,当前媒体库的处理策略是维护了一个白名单,白名单之外的格式会进行转码成jpg,由于heic、heif的压缩率比较高,所以会导致转码后的图片比原图大,所以返回给用户的图片也会比原图大,属于正常现象。

quality 属性目前只支持jpg、jpeg、webp、heic、heif 这五个格式。

目录结构

/rntpc_react-native-image-picker  # 项目根目录
├── harmony                          # 鸿蒙适配代码
│   ├── image_picker.har             # har 包
│   └── image_picker/                # 鸿蒙适配核心代码
│       └── src/
│           └── main/
│               ├── cpp/                         # C++ 原生模块
│               │   ├── CMakeLists.txt
│               │   ├── RNImagePickerPackage.h
│               │   ├── RNImagePickerTurboModule.cpp
│               │   └── RNImagePickerTurboModule.h
│               ├── ets/                         # ArkTS 核心实现代码
│               │   ├── ImagePickerPackage.ets
│               │   ├── ImagePickerTurboModule.ts
│               │   └── Logger.ts

├── src                               # RN 代码
│   └─ index.ts                      # 入口文件
│   └─ types.ts                # 类型文件
├── README.md                         # 中文安装使用方法
├── README_en.md                      # 英文安装使用方法                 

贡献代码

使用过程中发现任何问题都可以提交 Issue,当然,也非常欢迎提交 PR 。

开源协议

本项目基于 The MIT License (MIT) ,请自由地享受和参与开源。

项目介绍

基于 react-native-image-picker 的 OpenHarmony 适配版,用于从相册选择或相机拍摄图片/视频

定制我的领域